diff options
44 files changed, 602 insertions, 269 deletions
diff --git a/ci/install-dmd.ps1 b/ci/install-dmd.ps1 index aeacdf2..fd13317 100644 --- a/ci/install-dmd.ps1 +++ b/ci/install-dmd.ps1 @@ -9,8 +9,8 @@ $ProgressPreference = "SilentlyContinue" $dmd_install = "C:\D" $dmd_version_file = "C:\cache\DMD_LATEST" -#echo "Fetching latest DMD version..." if (!$Version) { + #echo "Fetching latest DMD version..." $dmd_latest_url = "http://downloads.dlang.org/releases/LATEST" $retries = 10 for ($i = 1; $i -le $retries; $i++) { diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile index 980ed53..d5f4816 100644 --- a/ciimage/Dockerfile +++ b/ciimage/Dockerfile @@ -20,6 +20,7 @@ RUN sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" \ && apt-get -y install --no-install-recommends wine-stable \ && apt-get -y install llvm-dev libclang-dev \ && apt-get -y install libgcrypt11-dev \ +&& apt-get -y install libgpgme-dev \ && apt-get -y install libhdf5-dev \ && dub fetch urld && dub build urld --compiler=gdc \ && dub fetch dubtestproject \ diff --git a/data/macros.meson b/data/macros.meson index 73a31ab..05d21e5 100644 --- a/data/macros.meson +++ b/data/macros.meson @@ -21,7 +21,6 @@ --sharedstatedir=%{_sharedstatedir} \ --wrap-mode=%{__meson_wrap_mode} \ --auto-features=%{__meson_auto_features} \ - -Db_ndebug=true \ %{_vpath_srcdir} %{_vpath_builddir} \ %{nil}} diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 47fce8b..2789ee0 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -200,7 +200,7 @@ wmf_dep = dependency('libwmf', method : 'config-tool') ## Dependencies using config tools [CUPS](#cups), [LLVM](#llvm), [pcap](#pcap), [WxWidgets](#wxwidgets), -[libwmf](#libwmf), [GCrypt](#libgcrypt), and GnuStep either do not provide pkg-config +[libwmf](#libwmf), [GCrypt](#libgcrypt), [GPGME](#gpgme), and GnuStep either do not provide pkg-config modules or additionally can be detected via a config tool (cups-config, llvm-config, libgcrypt-config, etc). Meson has native support for these tools, and they can be found like other dependencies: @@ -210,6 +210,7 @@ pcap_dep = dependency('pcap', version : '>=1.0') cups_dep = dependency('cups', version : '>=1.4') llvm_dep = dependency('llvm', version : '>=4.0') libgcrypt_dep = dependency('libgcrypt', version: '>= 1.8') +gpgme_dep = dependency('gpgme', version: '>= 1.0') ``` ## AppleFrameworks @@ -389,6 +390,12 @@ The `language` keyword may used. `method` may be `auto`, `config-tool` or `pkg-config`. +## GPGME + +*(added 0.51.0)* + +`method` may be `auto` or `config-tool`. + ## Python3 Python3 is handled specially by meson: @@ -483,6 +490,18 @@ Meson substitutes `modules` to `wx-config` invocation, it generates - `compile_args` using `wx-config --cxxflags $modules...` - `link_args` using `wx-config --libs $modules...` +## Shaderc + +*(added 0.51.0)* + +Shaderc currently does not ship with any means of detection. Nevertheless, Meson +can try to detect it using `pkg-config`, but will default to looking for the +appropriate library manually. If the `static` keyword argument is `true`, +`shaderc_combined` is preferred. Otherwise, `shaderc_shared` is preferred. Note +that it is not possible to obtain the shaderc version using this method. + +`method` may be `auto`, `pkg-config` or `system`. + ### Example ```meson diff --git a/docs/markdown/snippets/gpgme-config.md b/docs/markdown/snippets/gpgme-config.md new file mode 100644 index 0000000..08a7d38 --- /dev/null +++ b/docs/markdown/snippets/gpgme-config.md @@ -0,0 +1,3 @@ +## gpgme dependency now supports gpgme-config + +Previously, we could only detect GPGME with custom invocations of `gpgme-config`. Now we added support to Meson allowing us to use `dependency('gpgme')` instead. diff --git a/docs/markdown/snippets/sanity-check.md b/docs/markdown/snippets/sanity-check.md new file mode 100644 index 0000000..d5ed80d --- /dev/null +++ b/docs/markdown/snippets/sanity-check.md @@ -0,0 +1,17 @@ +## Sanity checking compilers with user flags + +Sanity checks previously only used user-specified flags for cross compilers, but +now do in all cases. + +All compilers meson might decide to use for the build are "sanity checked" +before other tests are run. This usually involves building simple executable and +trying to run it. Previously user flags (compilation and/or linking flags) were +used for sanity checking cross compilers, but not native compilers. This is +because such flags might be essential for a cross binary to succeed, but usually +aren't for a native compiler. + +In recent releases, there has been an effort to minimize the special-casing of +cross or native builds so as to make building more predictable in less-tested +cases. Since this the user flags are necessary for cross, but not harmful for +native, it makes more sense to use them in all sanity checks than use them in no +sanity checks, so this is what we now do. diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 01277f0..f1602c0 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -153,8 +153,20 @@ class AstInterpreter(interpreterbase.InterpreterBase): return True def evaluate_arithmeticstatement(self, cur): + self.evaluate_statement(cur.left) + self.evaluate_statement(cur.right) return 0 + def evaluate_uminusstatement(self, cur): + self.evaluate_statement(cur.value) + return 0 + + def evaluate_ternary(self, node): + assert(isinstance(node, mparser.TernaryNode)) + self.evaluate_statement(node.condition) + self.evaluate_statement(node.trueblock) + self.evaluate_statement(node.falseblock) + def evaluate_plusassign(self, node): assert(isinstance(node, mparser.PlusAssignmentNode)) if node.var_name not in self.assignments: @@ -177,6 +189,18 @@ class AstInterpreter(interpreterbase.InterpreterBase): return args.arguments, args.kwargs def evaluate_comparison(self, node): + self.evaluate_statement(node.left) + self.evaluate_statement(node.right) + return False + + def evaluate_andstatement(self, cur): + self.evaluate_statement(cur.left) + self.evaluate_statement(cur.right) + return False + + def evaluate_orstatement(self, cur): + self.evaluate_statement(cur.left) + self.evaluate_statement(cur.right) return False def evaluate_foreach(self, node): diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 5745d29..5a12e29 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -20,7 +20,7 @@ from .. import compilers, environment, mesonlib, optinterpreter from .. import coredata as cdata from ..interpreterbase import InvalidArguments from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary -from ..mparser import ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode +from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode import os build_target_functions = ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries'] @@ -142,6 +142,8 @@ class IntrospectionInterpreter(AstInterpreter): condition_level = node.condition_level if hasattr(node, 'condition_level') else 0 if isinstance(required, ElementaryNode): required = required.value + if not isinstance(required, bool): + required = False self.dependencies += [{ 'name': name, 'required': required, @@ -189,6 +191,8 @@ class IntrospectionInterpreter(AstInterpreter): # Make sure nothing can crash when creating the build class kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in ['install', 'build_by_default', 'build_always']} + kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in kwargs_reduced.items()} + kwargs_reduced = {k: v for k, v in kwargs_reduced.items() if not isinstance(v, BaseNode)} is_cross = False objects = [] empty_sources = [] # Passing the unresolved sources list causes errors diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d752ac4..04255dc 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -791,6 +791,7 @@ class Backend: for df in self.interpreter.get_build_def_files()] if self.environment.is_cross_build(): deps.extend(self.environment.coredata.cross_files) + deps.extend(self.environment.coredata.config_files) deps.append('meson-private/coredata.dat') if os.path.exists(os.path.join(self.environment.get_source_dir(), 'meson_options.txt')): deps.append(os.path.join(self.build_to_src, 'meson_options.txt')) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 2560c82..bcf8243 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -309,9 +309,9 @@ class CCompiler(Compiler): mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist)) mlog.debug('Is cross compiler: %s.' % str(self.is_cross)) - extra_flags = [] source_name = os.path.join(work_dir, sname) binname = sname.rsplit('.', 1)[0] + mode = 'link' if self.is_cross: binname += '_cross' if self.exe_wrapper is None: @@ -320,7 +320,9 @@ class CCompiler(Compiler): # on OSX the compiler binary is the same but you need # a ton of compiler flags to differentiate between # arm and x86_64. So just compile. - extra_flags += self.get_compile_only_args() + mode = 'compile' + extra_flags = self._get_basic_compiler_args(environment, mode) + # Is a valid executable output for all toolchains and platforms binname += '.exe' # Write binary check source @@ -392,6 +394,29 @@ class CCompiler(Compiler): return self.compiles(t.format(**fargs), env, extra_args=extra_args, dependencies=dependencies) + def _get_basic_compiler_args(self, env, mode): + args = [] + # Select a CRT if needed since we're linking + if mode == 'link': + args += self.get_linker_debug_crt_args() + if env.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + if mode in {'compile', 'preprocess'}: + # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env + sys_args = env.coredata.get_external_args(for_machine, self.language) + # Apparently it is a thing to inject linker flags both + # via CFLAGS _and_ LDFLAGS, even though the former are + # also used during linking. These flags can break + # argument checks. Thanks, Autotools. + cleaned_sys_args = self.remove_linkerlike_args(sys_args) + args += cleaned_sys_args + elif mode == 'link': + # Add LDFLAGS from the env + args += env.coredata.get_external_link_args(for_machine, self.language) + return args + def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): if extra_args is None: extra_args = [] @@ -415,25 +440,9 @@ class CCompiler(Compiler): args += d.get_link_args() if d.need_threads(): args += self.thread_link_flags(env) - # Select a CRT if needed since we're linking - if mode == 'link': - args += self.get_linker_debug_crt_args() - if env.is_cross_build() and not self.is_cross: - for_machine = MachineChoice.BUILD - else: - for_machine = MachineChoice.HOST - if mode in {'compile', 'preprocess'}: - # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env - sys_args = env.coredata.get_external_args(for_machine, self.language) - # Apparently it is a thing to inject linker flags both - # via CFLAGS _and_ LDFLAGS, even though the former are - # also used during linking. These flags can break - # argument checks. Thanks, Autotools. - cleaned_sys_args = self.remove_linkerlike_args(sys_args) - args += cleaned_sys_args - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.get_external_link_args(for_machine, self.language) + + args += self._get_basic_compiler_args(env, mode) + args += self.get_compiler_check_args() # extra_args must override all other arguments, so we add them last args += extra_args diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index f1580b6..529919b 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -622,7 +622,15 @@ class DmdDCompiler(DCompiler): return [] def get_std_shared_lib_link_args(self): - return ['-shared', '-defaultlib=libphobos2.so'] + libname = 'libphobos2.so' + if is_windows(): + if self.arch == 'x86_64': + libname = 'phobos64.lib' + elif self.arch == 'x86_mscoff': + libname = 'phobos32mscoff.lib' + else: + libname = 'phobos.lib' + return ['-shared', '-defaultlib=' + libname] def get_target_arch_args(self): # DMD32 and DMD64 on 64-bit Windows defaults to 32-bit (OMF). diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index a16f2b5..e747a35 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -31,7 +31,9 @@ from .compilers import ( PGICompiler ) -from mesonbuild.mesonlib import EnvironmentException, is_osx, LibType +from mesonbuild.mesonlib import ( + EnvironmentException, MachineChoice, is_osx, LibType +) class FortranCompiler(Compiler): @@ -74,12 +76,27 @@ class FortranCompiler(Compiler): def get_soname_args(self, *args): return CCompiler.get_soname_args(self, *args) - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanitycheckf.f90') - binary_name = os.path.join(work_dir, 'sanitycheckf') - with open(source_name, 'w') as ofile: - ofile.write('print *, "Fortran compilation is working."; end') - pc = subprocess.Popen(self.exelist + [source_name, '-o', binary_name]) + def sanity_check(self, work_dir: Path, environment): + """ + Check to be sure a minimal program can compile and execute + with this compiler & platform. + """ + work_dir = Path(work_dir) + source_name = work_dir / 'sanitycheckf.f90' + binary_name = work_dir / 'sanitycheckf' + if binary_name.is_file(): + binary_name.unlink() + + source_name.write_text('print *, "Fortran compilation is working."; end') + + if environment.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + extra_flags = environment.coredata.get_external_args(for_machine, self.language) + extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) + # %% build the test executable + pc = subprocess.Popen(self.exelist + extra_flags + [str(source_name), '-o', str(binary_name)]) pc.wait() if pc.returncode != 0: raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string()) @@ -87,12 +104,16 @@ class FortranCompiler(Compiler): if self.exe_wrapper is None: # Can't check if the binaries run so we have to assume they do return - cmdlist = self.exe_wrapper + [binary_name] + cmdlist = self.exe_wrapper + [str(binary_name)] else: - cmdlist = [binary_name] - pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - pe.wait() - if pe.returncode != 0: + cmdlist = [str(binary_name)] + # %% Run the test executable + try: + pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string()) + except OSError: raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string()) def get_std_warn_args(self, level): @@ -223,6 +244,9 @@ class FortranCompiler(Compiler): def gen_import_library_args(self, implibname): return CCompiler.gen_import_library_args(self, implibname) + def _get_basic_compiler_args(self, env, mode): + return CCompiler._get_basic_compiler_args(self, env, mode) + def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): return CCompiler._get_compiler_check_args(self, env, extra_args, dependencies, mode='compile') diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index 8dfd0a2..c4a7019 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -14,7 +14,7 @@ import os.path, subprocess -from ..mesonlib import EnvironmentException +from ..mesonlib import EnvironmentException, MachineChoice from .c import CCompiler from .compilers import ClangCompiler, GnuCompiler @@ -31,9 +31,15 @@ class ObjCCompiler(CCompiler): # TODO try to use sanity_check_impl instead of duplicated code source_name = os.path.join(work_dir, 'sanitycheckobjc.m') binary_name = os.path.join(work_dir, 'sanitycheckobjc') - extra_flags = [] + if environment.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + extra_flags = environment.coredata.get_external_args(for_machine, self.language) if self.is_cross: extra_flags += self.get_compile_only_args() + else: + extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) with open(source_name, 'w') as ofile: ofile.write('#import<stdio.h>\n' 'int main(int argc, char **argv) { return 0; }\n') diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index e66d730..4c23d0a 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -14,7 +14,7 @@ import os.path, subprocess -from ..mesonlib import EnvironmentException +from ..mesonlib import EnvironmentException, MachineChoice from .cpp import CPPCompiler from .compilers import ClangCompiler, GnuCompiler @@ -31,11 +31,20 @@ class ObjCPPCompiler(CPPCompiler): # TODO try to use sanity_check_impl instead of duplicated code source_name = os.path.join(work_dir, 'sanitycheckobjcpp.mm') binary_name = os.path.join(work_dir, 'sanitycheckobjcpp') + if environment.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + extra_flags = environment.coredata.get_external_args(for_machine, self.language) + if self.is_cross: + extra_flags += self.get_compile_only_args() + else: + extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) with open(source_name, 'w') as ofile: ofile.write('#import<stdio.h>\n' 'class MyClass;' 'int main(int argc, char **argv) { return 0; }\n') - pc = subprocess.Popen(self.exelist + [source_name, '-o', binary_name]) + pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) pc.wait() if pc.returncode != 0: raise EnvironmentException('ObjC++ compiler %s can not compile programs.' % self.name_string()) diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 94e6736..17705ac 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -14,7 +14,7 @@ import subprocess, os.path -from ..mesonlib import EnvironmentException +from ..mesonlib import EnvironmentException, MachineChoice from .compilers import Compiler, swift_buildtype_args, clike_debug_args @@ -102,13 +102,25 @@ class SwiftCompiler(Compiler): src = 'swifttest.swift' source_name = os.path.join(work_dir, src) output_name = os.path.join(work_dir, 'swifttest') + if environment.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + extra_flags = environment.coredata.get_external_args(for_machine, self.language) + if self.is_cross: + extra_flags += self.get_compile_only_args() + else: + extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) with open(source_name, 'w') as ofile: ofile.write('''print("Swift compilation is working.") ''') - pc = subprocess.Popen(self.exelist + ['-emit-executable', '-o', output_name, src], cwd=work_dir) + pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) pc.wait() if pc.returncode != 0: raise EnvironmentException('Swift compiler %s can not compile programs.' % self.name_string()) + if self.is_cross: + # Can't check if the binaries run so we have to assume they do + return if subprocess.call(output_name) != 0: raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string()) diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index b463f0d..98b8b42 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -15,7 +15,7 @@ import os.path from .. import mlog -from ..mesonlib import EnvironmentException, version_compare +from ..mesonlib import EnvironmentException, MachineChoice, version_compare from .compilers import Compiler @@ -87,7 +87,16 @@ class ValaCompiler(Compiler): def sanity_check(self, work_dir, environment): code = 'class MesonSanityCheck : Object { }' - with self.compile(code, [], 'compile') as p: + if environment.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + extra_flags = environment.coredata.get_external_args(for_machine, self.language) + if self.is_cross: + extra_flags += self.get_compile_only_args() + else: + extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) + with self.compile(code, extra_flags, 'compile') as p: if p.returncode != 0: msg = 'Vala compiler {!r} can not compile programs' \ ''.format(self.name_string()) @@ -105,8 +114,14 @@ class ValaCompiler(Compiler): # no extra dirs are specified. if not extra_dirs: code = 'class MesonFindLibrary : Object { }' + if env.is_cross_build() and not self.is_cross: + for_machine = MachineChoice.BUILD + else: + for_machine = MachineChoice.HOST + args = env.coredata.get_external_args(for_machine, self.language) vapi_args = ['--pkg', libname] - with self.compile(code, vapi_args, 'compile') as p: + args += vapi_args + with self.compile(code, args, 'compile') as p: if p.returncode == 0: return vapi_args # Not found? Try to find the vapi file itself. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index b5f48bd..739f6e7 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -26,6 +26,7 @@ from .wrap import WrapMode import ast import argparse import configparser +from typing import Optional, Any, TypeVar, Generic, Type version = '0.50.999' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode'] @@ -269,7 +270,6 @@ class CoreData: self.cross_compilers = OrderedDict() self.deps = OrderedDict() # Only to print a warning if it changes between Meson invocations. - self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') self.config_files = self.__load_config_files(options.native_file) self.libdir_cross_fixup() @@ -335,11 +335,10 @@ class CoreData: def init_builtins(self): # Create builtin options with default values self.builtins = {} - prefix = get_builtin_option_default('prefix') - for key in get_builtin_options(): - value = get_builtin_option_default(key, prefix) - args = [key] + builtin_options[key][1:-1] + [value] - self.builtins[key] = builtin_options[key][0](*args) + for key, opt in builtin_options.items(): + self.builtins[key] = opt.init_option(key) + if opt.separate_cross: + self.builtins['cross_' + key] = opt.init_option(key) def init_backend_options(self, backend_name): if backend_name == 'ninja': @@ -462,7 +461,7 @@ class CoreData: self.builtins['prefix'].set_value(prefix) for key in builtin_dir_noprefix_options: if key not in options: - self.builtins[key].set_value(get_builtin_option_default(key, prefix)) + self.builtins[key].set_value(builtin_options[key].prefixed_default(key, prefix)) unknown_options = [] for k, v in options.items(): @@ -495,7 +494,7 @@ class CoreData: from . import optinterpreter for k, v in default_options.items(): if subproject: - if optinterpreter.is_invalid_name(k): + if optinterpreter.is_invalid_name(k, log=False): continue k = subproject + ':' + k env.cmd_line_options.setdefault(k, v) @@ -506,14 +505,20 @@ class CoreData: # languages and setting the backend (builtin options must be set first # to know which backend we'll use). options = {} + + # Some options default to environment variables if they are + # unset, set those now. These will either be overwritten + # below, or they won't. + options['pkg_config_path'] = os.environ.get('PKG_CONFIG_PATH', '').split(':') + for k, v in env.cmd_line_options.items(): if subproject: if not k.startswith(subproject + ':'): continue - elif k not in get_builtin_options(): + elif k not in builtin_options: if ':' in k: continue - if optinterpreter.is_invalid_name(k): + if optinterpreter.is_invalid_name(k, log=False): continue options[k] = v @@ -531,7 +536,7 @@ class CoreData: if cross_comp is not None: new_options_for_host = cross_comp.get_and_default_options(env.properties.host) else: - new_options_for_host = comp.get_and_default_options(env.properties.host) + new_options_for_host = new_options_for_build opts_machines_list = [ (new_options_for_build, MachineChoice.BUILD), @@ -656,80 +661,10 @@ def save(obj, build_dir): os.replace(tempfilename, filename) return filename -def get_builtin_options(): - return list(builtin_options.keys()) - -def is_builtin_option(optname): - return optname in get_builtin_options() - -def get_builtin_option_choices(optname): - if is_builtin_option(optname): - if builtin_options[optname][0] == UserComboOption: - return builtin_options[optname][2] - elif builtin_options[optname][0] == UserBooleanOption: - return [True, False] - elif builtin_options[optname][0] == UserFeatureOption: - return UserFeatureOption.static_choices - else: - return None - else: - raise RuntimeError('Tried to get the supported values for an unknown builtin option \'%s\'.' % optname) - -def get_builtin_option_description(optname): - if is_builtin_option(optname): - return builtin_options[optname][1] - else: - raise RuntimeError('Tried to get the description for an unknown builtin option \'%s\'.' % optname) - -def get_builtin_option_action(optname): - default = builtin_options[optname][2] - if default is True: - return 'store_false' - elif default is False: - return 'store_true' - return None - -def get_builtin_option_default(optname, prefix=''): - if is_builtin_option(optname): - o = builtin_options[optname] - if o[0] == UserComboOption: - return o[3] - if o[0] == UserIntegerOption: - return o[4] - try: - return builtin_dir_noprefix_options[optname][prefix] - except KeyError: - pass - return o[2] - else: - raise RuntimeError('Tried to get the default value for an unknown builtin option \'%s\'.' % optname) - -def get_builtin_option_cmdline_name(name): - if name == 'warning_level': - return '--warnlevel' - else: - return '--' + name.replace('_', '-') - -def add_builtin_argument(p, name): - kwargs = {} - c = get_builtin_option_choices(name) - b = get_builtin_option_action(name) - h = get_builtin_option_description(name) - if not b: - h = h.rstrip('.') + ' (default: %s).' % get_builtin_option_default(name) - else: - kwargs['action'] = b - if c and not b: - kwargs['choices'] = c - kwargs['default'] = argparse.SUPPRESS - kwargs['dest'] = name - - cmdline_name = get_builtin_option_cmdline_name(name) - p.add_argument(cmdline_name, help=h, **kwargs) def register_builtin_arguments(parser): - for n in builtin_options: - add_builtin_argument(parser, n) + for n, b in builtin_options.items(): + b.add_to_argparse(n, parser) parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option", help='Set the value of an option, can be used several times to set multiple options.') @@ -747,48 +682,129 @@ def parse_cmd_line_options(args): args.cmd_line_options = create_options_dict(args.projectoptions) # Merge builtin options set with --option into the dict. - for name in builtin_options: - value = getattr(args, name, None) - if value is not None: - if name in args.cmd_line_options: - cmdline_name = get_builtin_option_cmdline_name(name) - raise MesonException( - 'Got argument {0} as both -D{0} and {1}. Pick one.'.format(name, cmdline_name)) - args.cmd_line_options[name] = value - delattr(args, name) + for name, builtin in builtin_options.items(): + names = [name] + if builtin.separate_cross: + names.append('cross_' + name) + for name in names: + value = getattr(args, name, None) + if value is not None: + if name in args.cmd_line_options: + cmdline_name = BuiltinOption.argparse_name_to_arg(name) + raise MesonException( + 'Got argument {0} as both -D{0} and {1}. Pick one.'.format(name, cmdline_name)) + args.cmd_line_options[name] = value + delattr(args, name) + + +_U = TypeVar('_U', bound=UserOption) + +class BuiltinOption(Generic[_U]): + + """Class for a builtin option type. + + Currently doesn't support UserIntegerOption, or a few other cases. + """ + + def __init__(self, opt_type: Type[_U], description: str, default: Any, yielding: Optional[bool] = None, *, + choices: Any = None, separate_cross: bool = False): + self.opt_type = opt_type + self.description = description + self.default = default + self.choices = choices + self.yielding = yielding + self.separate_cross = separate_cross + + def init_option(self, name: str) -> _U: + """Create an instance of opt_type and return it.""" + keywords = {'yielding': self.yielding, 'value': self.default} + if self.choices: + keywords['choices'] = self.choices + return self.opt_type(name, self.description, **keywords) + + def _argparse_action(self) -> Optional[str]: + if self.default is True: + return 'store_false' + elif self.default is False: + return 'store_true' + return None + + def _argparse_choices(self) -> Any: + if self.opt_type is UserBooleanOption: + return [True, False] + elif self.opt_type is UserFeatureOption: + return UserFeatureOption.static_choices + return self.choices + + @staticmethod + def argparse_name_to_arg(name: str) -> str: + if name == 'warning_level': + return '--warnlevel' + else: + return '--' + name.replace('_', '-') + + def prefixed_default(self, name: str, prefix: str = '') -> Any: + if self.opt_type in [UserComboOption, UserIntegerOption]: + return self.default + try: + return builtin_dir_noprefix_options[name][prefix] + except KeyError: + pass + return self.default + + def add_to_argparse(self, name: str, parser: argparse.ArgumentParser) -> None: + kwargs = {} + + c = self._argparse_choices() + b = self._argparse_action() + h = self.description + if not b: + h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name)) + else: + kwargs['action'] = b + if c and not b: + kwargs['choices'] = c + kwargs['default'] = argparse.SUPPRESS + kwargs['dest'] = name + + cmdline_name = self.argparse_name_to_arg(name) + parser.add_argument(cmdline_name, help=h, **kwargs) + if self.separate_cross: + kwargs['dest'] = 'cross_' + name + parser.add_argument(self.argparse_name_to_arg('cross_' + name), help=h + ' (for host in cross compiles)', **kwargs) + builtin_options = { - 'buildtype': [UserComboOption, 'Build type to use', ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'], 'debug'], - 'strip': [UserBooleanOption, 'Strip targets on install', False], - 'unity': [UserComboOption, 'Unity build', ['on', 'off', 'subprojects'], 'off'], - 'prefix': [UserStringOption, 'Installation prefix', default_prefix()], - 'libdir': [UserStringOption, 'Library directory', default_libdir()], - 'libexecdir': [UserStringOption, 'Library executable directory', default_libexecdir()], - 'bindir': [UserStringOption, 'Executable directory', 'bin'], - 'sbindir': [UserStringOption, 'System executable directory', 'sbin'], - 'includedir': [UserStringOption, 'Header file directory', 'include'], - 'datadir': [UserStringOption, 'Data file directory', 'share'], - 'mandir': [UserStringOption, 'Manual page directory', 'share/man'], - 'infodir': [UserStringOption, 'Info page directory', 'share/info'], - 'localedir': [UserStringOption, 'Locale data directory', 'share/locale'], - 'sysconfdir': [UserStringOption, 'Sysconf data directory', 'etc'], - 'localstatedir': [UserStringOption, 'Localstate data directory', 'var'], - 'sharedstatedir': [UserStringOption, 'Architecture-independent data directory', 'com'], - 'werror': [UserBooleanOption, 'Treat warnings as errors', False], - 'warning_level': [UserComboOption, 'Compiler warning level to use', ['0', '1', '2', '3'], '1'], - 'layout': [UserComboOption, 'Build directory layout', ['mirror', 'flat'], 'mirror'], - 'default_library': [UserComboOption, 'Default library type', ['shared', 'static', 'both'], 'shared'], - 'backend': [UserComboOption, 'Backend to use', backendlist, 'ninja'], - 'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs', True], - 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests", True], - 'install_umask': [UserUmaskOption, 'Default umask to apply on permissions of installed files', '022'], - 'auto_features': [UserFeatureOption, "Override value of all 'auto' features", 'auto'], - 'optimization': [UserComboOption, 'Optimization level', ['0', 'g', '1', '2', '3', 's'], '0'], - 'debug': [UserBooleanOption, 'Debug', True], - 'wrap_mode': [UserComboOption, 'Wrap mode', ['default', - 'nofallback', - 'nodownload', - 'forcefallback'], 'default'], + 'buildtype': BuiltinOption(UserComboOption, 'Build type to use', 'debug', + choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']), + 'strip': BuiltinOption(UserBooleanOption, 'Strip targets on install', False), + 'unity': BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects']), + 'prefix': BuiltinOption(UserStringOption, 'Installation prefix', default_prefix()), + 'libdir': BuiltinOption(UserStringOption, 'Library directory', default_libdir()), + 'libexecdir': BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir()), + 'bindir': BuiltinOption(UserStringOption, 'Executable directory', 'bin'), + 'sbindir': BuiltinOption(UserStringOption, 'System executable directory', 'sbin'), + 'includedir': BuiltinOption(UserStringOption, 'Header file directory', 'include'), + 'datadir': BuiltinOption(UserStringOption, 'Data file directory', 'share'), + 'mandir': BuiltinOption(UserStringOption, 'Manual page directory', 'share/man'), + 'infodir': BuiltinOption(UserStringOption, 'Info page directory', 'share/info'), + 'localedir': BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale'), + 'sysconfdir': BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc'), + 'localstatedir': BuiltinOption(UserStringOption, 'Localstate data directory', 'var'), + 'sharedstatedir': BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com'), + 'werror': BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False), + 'warning_level': BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3']), + 'layout': BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat']), + 'default_library': BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both']), + 'backend': BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist), + 'stdsplit': BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True), + 'errorlogs': BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True), + 'install_umask': BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022'), + 'auto_features': BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto'), + 'optimization': BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's']), + 'debug': BuiltinOption(UserBooleanOption, 'Debug', True), + 'wrap_mode': BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback']), + 'pkg_config_path': BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [], separate_cross=True), } # Special prefix-dependent defaults for installation directories that reside in diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 53ff1c9..846f3de 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -18,7 +18,7 @@ from .base import ( # noqa: F401 ExternalDependency, NotFoundDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, PkgConfigDependency, CMakeDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language) from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency -from .misc import (CoarrayDependency, HDF5Dependency, MPIDependency, NetCDFDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency, LibGCryptDependency) +from .misc import (CoarrayDependency, HDF5Dependency, MPIDependency, NetCDFDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency, LibGCryptDependency, GpgmeDependency, ShadercDependency) from .platform import AppleFrameworks from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency @@ -43,6 +43,8 @@ packages.update({ 'cups': CupsDependency, 'libwmf': LibWmfDependency, 'libgcrypt': LibGCryptDependency, + 'gpgme': GpgmeDependency, + 'shaderc': ShadercDependency, # From platform: 'appleframeworks': AppleFrameworks, diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index af4b13f..f74eabb 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -419,6 +419,14 @@ class ConfigToolDependency(ExternalDependency): best_match = (None, None) for tool in tools: + if len(tool) == 1: + # In some situations the command can't be directly executed. + # For example Shell scripts need to be called through sh on + # Windows (see issue #1423). + potential_bin = ExternalProgram(tool[0], silent=True) + if not potential_bin.found(): + continue + tool = potential_bin.get_command() try: p, out = Popen_safe(tool + ['--version'])[:2] except (FileNotFoundError, PermissionError): @@ -459,7 +467,7 @@ class ConfigToolDependency(ExternalDependency): elif req_version: found_msg.append('need {!r}'.format(req_version)) else: - found_msg += [mlog.green('YES'), '({})'.format(shutil.which(self.config[0])), version] + found_msg += [mlog.green('YES'), '({})'.format(' '.join(self.config)), version] mlog.log(*found_msg) @@ -609,11 +617,19 @@ class PkgConfigDependency(ExternalDependency): return rc, out def _call_pkgbin(self, args, env=None): + # Always copy the environment since we're going to modify it + # with pkg-config variables if env is None: - fenv = env - env = os.environ + env = os.environ.copy() else: - fenv = frozenset(env.items()) + env = env.copy() + + if self.want_cross: + extra_paths = self.env.coredata.get_builtin_option('cross_pkg_config_path') + else: + extra_paths = self.env.coredata.get_builtin_option('pkg_config_path') + env['PKG_CONFIG_PATH'] = ':'.join([p for p in extra_paths]) + fenv = frozenset(env.items()) targs = tuple(args) cache = PkgConfigDependency.pkgbin_cache if (self.pkgbin, targs, fenv) not in cache: diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 52e8ee6..f4694ca 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -117,6 +117,7 @@ class HDF5Dependency(ExternalDependency): except Exception: pass + class NetCDFDependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -211,6 +212,9 @@ class MPIDependency(ExternalDependency): break if not self.is_found and mesonlib.is_windows(): + # only Intel Fortran compiler is compatible with Microsoft MPI at this time. + if language == 'fortran' and environment.detect_fortran_compiler(False).name_string() != 'intel': + return result = self._try_msmpi() if result is not None: self.is_found = True @@ -666,3 +670,77 @@ class LibGCryptDependency(ExternalDependency): @staticmethod def get_methods(): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] + + +class GpgmeDependency(ExternalDependency): + def __init__(self, environment, kwargs): + super().__init__('gpgme', environment, None, kwargs) + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + candidates = [] + + if DependencyMethods.CONFIG_TOOL in methods: + candidates.append(functools.partial(ConfigToolDependency.factory, + 'gpgme', environment, None, kwargs, ['gpgme-config'], + 'gpgme-config', + GpgmeDependency.tool_finish_init)) + + return candidates + + @staticmethod + def tool_finish_init(ctdep): + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') + ctdep.version = ctdep.get_config_value(['--version'], 'version')[0] + + @staticmethod + def get_methods(): + return [DependencyMethods.CONFIG_TOOL] + + +class ShadercDependency(ExternalDependency): + + def __init__(self, environment, kwargs): + super().__init__('shaderc', environment, None, kwargs) + + static_lib = 'shaderc_combined' + shared_lib = 'shaderc_shared' + + libs = [shared_lib, static_lib] + if self.static: + libs.reverse() + + cc = self.get_compiler() + + for lib in libs: + self.link_args = cc.find_library(lib, environment, []) + if self.link_args is not None: + self.is_found = True + + if self.static and lib != static_lib: + mlog.warning('Static library {!r} not found for dependency {!r}, may ' + 'not be statically linked'.format(static_lib, self.name)) + + break + + def log_tried(self): + return 'system' + + @classmethod + def _factory(cls, environment, kwargs): + methods = cls._process_method_kw(kwargs) + candidates = [] + + if DependencyMethods.SYSTEM in methods: + candidates.append(functools.partial(ShadercDependency, environment, kwargs)) + + if DependencyMethods.PKGCONFIG in methods: + candidates.append(functools.partial(PkgConfigDependency, 'shaderc', environment, kwargs)) + + return candidates + + @staticmethod + def get_methods(): + return [DependencyMethods.SYSTEM, DependencyMethods.PKGCONFIG] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index deb6b7d..2241089 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -215,6 +215,9 @@ def detect_cpu_family(compilers): trial = 'x86' elif trial == 'bepc': trial = 'x86' + # OpenBSD's 64 bit arm architecute identifies as 'arm64' + elif trial == 'arm64': + trial = 'aarch64' elif trial.startswith('arm'): trial = 'arm' elif trial.startswith('ppc64'): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 234b983..4b713ea 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1911,7 +1911,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'}, 'add_languages': {'required'}, 'add_project_link_arguments': {'language', 'native'}, 'add_project_arguments': {'language', 'native'}, - 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, + 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'build_target': known_build_target_kwargs, 'configure_file': {'input', @@ -2471,7 +2471,8 @@ external dependencies (including libraries) must go to "dependencies".''') self.active_projectname = current_active self.subprojects.update(subi.subprojects) self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname) - self.build_def_files += subi.build_def_files + # Duplicates are possible when subproject uses files from project root + self.build_def_files = list(set(self.build_def_files + subi.build_def_files)) self.build.merge(subi.build) self.build.subprojects[dirname] = subi.project_version return self.subprojects[dirname] diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 34fe5a5..bd69244 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -131,7 +131,7 @@ class PythonDependency(ExternalDependency): if self.is_found: mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES ({})'.format(py_lookup_method))) else: - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) + mlog.log('Dependency', mlog.bold(self.name), 'found:', [mlog.red('NO')]) def _find_libpy(self, python_holder, environment): if python_holder.is_pypy: @@ -498,9 +498,6 @@ class PythonModule(ExtensionModule): def find_installation(self, interpreter, state, args, kwargs): feature_check = FeatureNew('Passing "feature" option to find_installation', '0.48.0') disabled, required, feature = extract_required_kwarg(kwargs, state.subproject, feature_check) - if disabled: - mlog.log('find_installation skipped: feature', mlog.bold(feature), 'disabled') - return ExternalProgramHolder(NonExistingExternalProgram()) if len(args) > 1: raise InvalidArguments('find_installation takes zero or one positional argument.') @@ -511,9 +508,12 @@ class PythonModule(ExtensionModule): if not isinstance(name_or_path, str): raise InvalidArguments('find_installation argument must be a string.') + if disabled: + mlog.log('Program', name_or_path or 'python', 'found:', mlog.red('NO'), '(disabled by:', mlog.bold(feature), ')') + return ExternalProgramHolder(NonExistingExternalProgram()) + if not name_or_path: - mlog.log("Using meson's python {}".format(mesonlib.python_command)) - python = ExternalProgram('python3', mesonlib.python_command, silent=True) + python = ExternalProgram('python3', mesonlib.python_command) else: python = ExternalProgram.from_entry('python3', name_or_path) @@ -521,14 +521,17 @@ class PythonModule(ExtensionModule): pythonpath = self._get_win_pythonpath(name_or_path) if pythonpath is not None: name_or_path = pythonpath - python = ExternalProgram(name_or_path, silent = True) + python = ExternalProgram(name_or_path, silent=True) # Last ditch effort, python2 or python3 can be named python # on various platforms, let's not give up just yet, if an executable # named python is available and has a compatible version, let's use # it if not python.found() and name_or_path in ['python2', 'python3']: - python = ExternalProgram('python', silent = True) + python = ExternalProgram('python', silent=True) + + mlog.log('Program', python.name, 'found:', + *[mlog.green('YES'), '({})'.format(' '.join(python.command))] if python.found() else [mlog.red('NO')]) if not python.found(): if required: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 6e8ca83..ef0511d 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -149,13 +149,6 @@ class MesonApp: sys.exit(1) return src_dir, build_dir - def check_pkgconfig_envvar(self, env): - curvar = os.environ.get('PKG_CONFIG_PATH', '') - if curvar != env.coredata.pkgconf_envvar: - mlog.warning('PKG_CONFIG_PATH has changed between invocations from "%s" to "%s".' % - (env.coredata.pkgconf_envvar, curvar)) - env.coredata.pkgconf_envvar = curvar - def generate(self): env = environment.Environment(self.source_dir, self.build_dir, self.options) mlog.initialize(env.get_log_dir(), self.options.fatal_warnings) @@ -169,7 +162,6 @@ class MesonApp: mlog.debug('Main binary:', sys.executable) mlog.debug('Python system:', platform.system()) mlog.log(mlog.bold('The Meson build system')) - self.check_pkgconfig_envvar(env) mlog.log('Version:', coredata.version) mlog.log('Source dir:', mlog.bold(self.source_dir)) mlog.log('Build dir:', mlog.bold(self.build_dir)) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index dc82084..0f15690 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -490,7 +490,9 @@ class SingleTestRunner: stderr = None if not self.options.verbose: stdout = tempfile.TemporaryFile("wb+") - stderr = tempfile.TemporaryFile("wb+") if self.options and self.options.split else stdout + stderr = tempfile.TemporaryFile("wb+") if self.options.split else stdout + if self.test.protocol == 'tap' and stderr is stdout: + stdout = tempfile.TemporaryFile("wb+") # Let gdb handle ^C instead of us if self.options.gdb: @@ -570,17 +572,16 @@ class SingleTestRunner: endtime = time.time() duration = endtime - starttime if additional_error is None: - if stdout is None: # if stdout is None stderr should be as well + if stdout is None: stdo = '' - stde = '' else: stdout.seek(0) stdo = decode(stdout.read()) - if stderr != stdout: - stderr.seek(0) - stde = decode(stderr.read()) - else: - stde = "" + if stderr is None or stderr is stdout: + stde = '' + else: + stderr.seek(0) + stde = decode(stderr.read()) else: stdo = "" stde = additional_error @@ -590,6 +591,8 @@ class SingleTestRunner: if self.test.protocol == 'exitcode': return TestRun.make_exitcode(self.test, p.returncode, duration, stdo, stde, cmd) else: + if self.options.verbose: + print(stdo, end='') return TestRun.make_tap(self.test, p.returncode, duration, stdo, stde, cmd) diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 85f6897..e64ed4e 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -20,19 +20,20 @@ from . import coredata from . import mesonlib from . import compilers -forbidden_option_names = coredata.get_builtin_options() +forbidden_option_names = set(coredata.builtin_options.keys()) forbidden_prefixes = [lang + '_' for lang in compilers.all_languages] + ['b_', 'backend_'] reserved_prefixes = ['cross_'] -def is_invalid_name(name): +def is_invalid_name(name: str, *, log: bool = True) -> bool: if name in forbidden_option_names: return True pref = name.split('_')[0] + '_' if pref in forbidden_prefixes: return True if pref in reserved_prefixes: - from . import mlog - mlog.deprecation('Option uses prefix "%s", which is reserved for Meson. This will become an error in the future.' % pref) + if log: + from . import mlog + mlog.deprecation('Option uses prefix "%s", which is reserved for Meson. This will become an error in the future.' % pref) return False class OptionException(mesonlib.MesonException): diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index 976d2f0..95ea0ec 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -68,7 +68,14 @@ def linux_syms(libfilename, outfilename): libfilename])[0:2] if pnm.returncode != 0: raise RuntimeError('nm does not work.') - result += [' '.join(x.split()[0:2]) for x in output.split('\n') if len(x) > 0] + for line in output.split('\n'): + if len(line) == 0: + continue + line_split = line.split() + entry = line_split[0:2] + if len(line_split) >= 4: + entry += [line_split[3]] + result += [' '.join(entry)] write_if_changed('\n'.join(result) + '\n', outfilename) def osx_syms(libfilename, outfilename): diff --git a/mesonbuild/scripts/vcstagger.py b/mesonbuild/scripts/vcstagger.py index 62a45d9..16dd4d1 100644 --- a/mesonbuild/scripts/vcstagger.py +++ b/mesonbuild/scripts/vcstagger.py @@ -14,6 +14,7 @@ import sys, os, subprocess, re + def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, cmd): try: output = subprocess.check_output(cmd, cwd=source_dir) @@ -21,17 +22,18 @@ def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_ except Exception: new_string = fallback - with open(infile) as f: + with open(infile, encoding='utf8') as f: new_data = f.read().replace(replace_string, new_string) if os.path.exists(outfile): - with open(outfile) as f: + with open(outfile, encoding='utf8') as f: needs_update = (f.read() != new_data) else: needs_update = True if needs_update: - with open(outfile, 'w') as f: + with open(outfile, 'w', encoding='utf8') as f: f.write(new_data) + def run(args): infile, outfile, fallback, source_dir, replace_string, regex_selector = args[0:6] command = args[6:] diff --git a/run_cross_test.py b/run_cross_test.py index b2ef6be..8d18123 100755 --- a/run_cross_test.py +++ b/run_cross_test.py @@ -34,7 +34,7 @@ def runtests(cross_file, failfast): commontests = [('common', gather_tests(Path('test cases', 'common')), False)] try: (passing_tests, failing_tests, skipped_tests) = \ - run_tests(commontests, 'meson-cross-test-run', failfast, ['--cross', cross_file]) + run_tests(commontests, 'meson-cross-test-run', failfast, ['--cross-file', cross_file]) except StopException: pass print('\nTotal passed cross tests:', passing_tests) diff --git a/run_project_tests.py b/run_project_tests.py index 467d522..fdb5f48 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -460,6 +460,7 @@ def have_objc_compiler(): return False if not objc_comp: return False + env.coredata.process_new_compilers('objc', objc_comp, None, env) try: objc_comp.sanity_check(env.get_scratch_dir(), env) except mesonlib.MesonException: @@ -475,6 +476,7 @@ def have_objcpp_compiler(): return False if not objcpp_comp: return False + env.coredata.process_new_compilers('objcpp', objcpp_comp, None, env) try: objcpp_comp.sanity_check(env.get_scratch_dir(), env) except mesonlib.MesonException: @@ -561,7 +563,7 @@ def detect_tests_to_run(): ('C#', 'csharp', skip_csharp(backend)), ('vala', 'vala', backend is not Backend.ninja or not shutil.which('valac')), ('rust', 'rust', backend is not Backend.ninja or not shutil.which('rustc')), - ('d', 'd', backend is not Backend.ninja or not have_d_compiler() or mesonlib.is_windows()), + ('d', 'd', backend is not Backend.ninja or not have_d_compiler()), ('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or mesonlib.is_windows() or not have_objc_compiler()), ('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or mesonlib.is_windows() or not have_objcpp_compiler()), ('fortran', 'fortran', backend is not Backend.ninja or not shutil.which('gfortran')), diff --git a/run_unittests.py b/run_unittests.py index f381efc..19426b8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3408,7 +3408,7 @@ recommended as it is not supported on some platforms''') self.assertDictEqual(buildopts_to_find, {}) # Check buildsystem_files - bs_files = ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build'] + bs_files = ['meson.build', 'meson_options.txt', 'sharedlib/meson.build', 'staticlib/meson.build'] bs_files = [os.path.join(testdir, x) for x in bs_files] self.assertPathListEqual(list(sorted(res['buildsystem_files'])), list(sorted(bs_files))) @@ -3560,6 +3560,12 @@ recommended as it is not supported on some platforms''') 'conditional': False }, { + 'name': 'bugDep1', + 'required': False, + 'has_fallback': False, + 'conditional': False + }, + { 'name': 'somethingthatdoesnotexist', 'required': True, 'has_fallback': False, @@ -4373,12 +4379,11 @@ class LinuxlikeTests(BasePlatformTests): cmd_std = '-std=FAIL' env_flags = p.upper() + 'FLAGS' os.environ[env_flags] = cmd_std - self.init(testdir) - cmd = self.get_compdb()[0]['command'] - qcmd_std = " {} ".format(cmd_std) - self.assertIn(qcmd_std, cmd) - with self.assertRaises(subprocess.CalledProcessError, - msg='{} should have failed'.format(qcmd_std)): + with self.assertRaises((subprocess.CalledProcessError, mesonbuild.mesonlib.EnvironmentException), + msg='C compiler should have failed with -std=FAIL'): + self.init(testdir) + # ICC won't fail in the above because additional flags are needed to + # make unknown -std=... options errors. self.build() def test_compiler_c_stds(self): @@ -4832,9 +4837,9 @@ endian = 'little' testdir = os.path.join(self.unit_test_dir, '58 pkgconfig relative paths') pkg_dir = os.path.join(testdir, 'pkgconfig') self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'librelativepath.pc'))) - os.environ['PKG_CONFIG_PATH'] = pkg_dir env = get_fake_env(testdir, self.builddir, self.prefix) + env.coredata.set_options({'pkg_config_path': pkg_dir}, '') kwargs = {'required': True, 'silent': True} relative_path_dep = PkgConfigDependency('librelativepath', env, kwargs) self.assertTrue(relative_path_dep.found()) @@ -4873,7 +4878,7 @@ endian = 'little' myenv['PKG_CONFIG_PATH'] = self.privatedir stdo = subprocess.check_output(['pkg-config', '--libs-only-l', 'libsomething'], env=myenv) deps = [b'-lgobject-2.0', b'-lgio-2.0', b'-lglib-2.0', b'-lsomething'] - if is_windows() or is_cygwin() or is_osx(): + if is_windows() or is_cygwin() or is_osx() or is_openbsd(): # On Windows, libintl is a separate library deps.append(b'-lintl') self.assertEqual(set(deps), set(stdo.split())) @@ -5063,6 +5068,11 @@ endian = 'little' # Assert that self.assertEqual(len(line.split(lib)), 2, msg=(lib, line)) + @skipIfNoPkgconfig + def test_pkg_config_option(self): + testdir = os.path.join(self.unit_test_dir, '55 pkg_config_path option') + self.init(testdir, extra_args=['-Dpkg_config_path=' + os.path.join(testdir, 'extra_path')]) + def should_run_cross_arm_tests(): return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') @@ -5159,6 +5169,11 @@ class LinuxCrossMingwTests(BasePlatformTests): # Must run in-process or we'll get a generic CalledProcessError self.run_tests(inprocess=True) + @skipIfNoPkgconfig + def test_cross_pkg_config_option(self): + testdir = os.path.join(self.unit_test_dir, '55 pkg_config_path option') + self.init(testdir, extra_args=['-Dcross_pkg_config_path=' + os.path.join(testdir, 'extra_path')]) + class PythonTests(BasePlatformTests): ''' diff --git a/test cases/common/177 initial c_args/meson.build b/test cases/common/177 initial c_args/meson.build index 169d2bd..638f8c2 100644 --- a/test cases/common/177 initial c_args/meson.build +++ b/test cases/common/177 initial c_args/meson.build @@ -3,5 +3,5 @@ project('options', 'c') # Test passing c_args and c_link_args options from the command line. assert(get_option('c_args') == ['-funroll-loops'], 'Incorrect value for c_args option.') -assert(get_option('c_link_args') == ['-random_linker_option'], +assert(get_option('c_link_args') == ['-Dtest_harmless_but_useless_link_arg'], 'Incorrect value for c_link_args option.') diff --git a/test cases/common/177 initial c_args/test_args.txt b/test cases/common/177 initial c_args/test_args.txt index 9a6da06..166e481 100644 --- a/test cases/common/177 initial c_args/test_args.txt +++ b/test cases/common/177 initial c_args/test_args.txt @@ -1,4 +1,4 @@ # This file is not read by meson itself, but by the test framework. # It is not possible to pass arguments to meson from a file. ['-Dc_args=-march=native', '-Dc_args=-funroll-loops', - '-Dc_link_args=-random_linker_option'] + '-Dc_link_args=-Dtest_harmless_but_useless_link_arg'] diff --git a/test cases/failing/85 gtest dependency with version/meson.build b/test cases/failing/85 gtest dependency with version/meson.build index 5115f27..3d90994 100644 --- a/test cases/failing/85 gtest dependency with version/meson.build +++ b/test cases/failing/85 gtest dependency with version/meson.build @@ -1,3 +1,3 @@ project('gtest dependency with version', ['c', 'cpp']) # discovering gtest version is not yet implemented -dep = dependency('gtest', version: '>0') +dep = dependency('gtest', method: 'system', version: '>0') diff --git a/test cases/frameworks/17 mpi/is_broken_ubuntu.py b/test cases/frameworks/17 mpi/is_broken_ubuntu.py deleted file mode 100755 index 27651ba..0000000 --- a/test cases/frameworks/17 mpi/is_broken_ubuntu.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 - -# Any exception causes return value to be not zero, which is sufficient. - -import sys - -fc = open('/etc/apt/sources.list').read() -if 'artful' not in fc and 'bionic' not in fc and 'cosmic' not in fc: - sys.exit(1) diff --git a/test cases/frameworks/17 mpi/main.f90 b/test cases/frameworks/17 mpi/main.f90 index d379e96..b5666e8 100644 --- a/test cases/frameworks/17 mpi/main.f90 +++ b/test cases/frameworks/17 mpi/main.f90 @@ -1,21 +1,30 @@ -program mpitest - implicit none - include 'mpif.h' - logical :: flag - integer :: ier - call MPI_Init(ier) - if (ier /= 0) then - print *, 'Unable to initialize MPI: ', ier - stop 1 - endif - call MPI_Initialized(flag, ier) - if (ier /= 0) then - print *, 'Unable to check MPI initialization state: ', ier - stop 1 - endif - call MPI_Finalize(ier) - if (ier /= 0) then - print *, 'Unable to finalize MPI: ', ier - stop 1 - endif -end program mpitest +use, intrinsic :: iso_fortran_env, only: stderr=>error_unit +use mpi + +implicit none + +logical :: flag +integer :: ier + +call MPI_Init(ier) + +if (ier /= 0) then + write(stderr,*) 'Unable to initialize MPI', ier + stop 1 +endif + +call MPI_Initialized(flag, ier) +if (ier /= 0) then + write(stderr,*) 'Unable to check MPI initialization state: ', ier + stop 1 +endif + +call MPI_Finalize(ier) +if (ier /= 0) then + write(stderr,*) 'Unable to finalize MPI: ', ier + stop 1 +endif + +print *, "OK: Fortran MPI" + +end program diff --git a/test cases/frameworks/17 mpi/meson.build b/test cases/frameworks/17 mpi/meson.build index 2d0e4d3..8af9374 100644 --- a/test cases/frameworks/17 mpi/meson.build +++ b/test cases/frameworks/17 mpi/meson.build @@ -26,21 +26,15 @@ if build_machine.system() != 'windows' test('MPI C++', execpp) endif -# OpenMPI is broken with Fortran on Ubuntu Artful. -# Remove this once the following bug has been fixed: -# -# https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1727474 - -ubudetector = find_program('is_broken_ubuntu.py') -uburesult = run_command(ubudetector) - -if uburesult.returncode() != 0 and add_languages('fortran', required : false) - mpifort = dependency('mpi', language : 'fortran') - # Mixing compilers (msvc/clang with gfortran) does not seem to work on Windows. - if build_machine.system() != 'windows' or cc.get_id() == 'gnu' +# One of few feasible ways to use MPI for Fortran on Windows is via Intel compilers. +if build_machine.system() != 'windows' or cc.get_id() == 'intel' + if add_languages('fortran', required : false) + mpifort = dependency('mpi', language : 'fortran') + exef = executable('exef', - 'main.f90', - dependencies : [mpifort]) + 'main.f90', + dependencies : [mpifort]) + test('MPI Fortran', exef) endif endif diff --git a/test cases/frameworks/27 gpgme/gpgme_prog.c b/test cases/frameworks/27 gpgme/gpgme_prog.c new file mode 100644 index 0000000..594f685 --- /dev/null +++ b/test cases/frameworks/27 gpgme/gpgme_prog.c @@ -0,0 +1,8 @@ +#include <gpgme.h> + +int +main() +{ + printf("gpgme-v%s", gpgme_check_version(NULL)); + return 0; +} diff --git a/test cases/frameworks/27 gpgme/meson.build b/test cases/frameworks/27 gpgme/meson.build new file mode 100644 index 0000000..220a4c0 --- /dev/null +++ b/test cases/frameworks/27 gpgme/meson.build @@ -0,0 +1,21 @@ +project('gpgme test', 'c') + +wm = find_program('gpgme-config', required: false) +if not wm.found() + error('MESON_SKIP_TEST: gpgme-config not installed') +endif + +gpgme_dep = dependency('gpgme', version: '>= 1.0') +gpgme_ver = gpgme_dep.version() +assert(gpgme_ver.split('.').length() > 1, 'gpgme version is "@0@"'.format(gpgme_ver)) +message('gpgme version is "@0@"'.format(gpgme_ver)) +e = executable('gpgme_prog', 'gpgme_prog.c', dependencies: gpgme_dep) + +test('gpgmetest', e) + +# Test using the method keyword: + +dependency('gpgme', method: 'config-tool') + +# Check we can apply a version constraint +dependency('gpgme', version: '>=@0@'.format(gpgme_dep.version()), method: 'config-tool') diff --git a/test cases/python/1 basic/meson.build b/test cases/python/1 basic/meson.build index f9a7433..9c3af10 100644 --- a/test cases/python/1 basic/meson.build +++ b/test cases/python/1 basic/meson.build @@ -1,7 +1,7 @@ project('python sample', 'c') py_mod = import('python') -py = py_mod.find_installation() +py = py_mod.find_installation('python3') py_version = py.language_version() if py_version.version_compare('< 3.2') diff --git a/test cases/unit/55 introspection/meson.build b/test cases/unit/55 introspection/meson.build index 98f6f22..588f71c 100644 --- a/test cases/unit/55 introspection/meson.build +++ b/test cases/unit/55 introspection/meson.build @@ -2,6 +2,12 @@ project('introspection', ['c', 'cpp'], version: '1.2.3', default_options: ['cpp_ dep1 = dependency('threads') dep2 = dependency('zlib', required: false) +dep3 = dependency('bugDep1', required: get_option('test_opt1')) + +b1 = get_option('test_opt1') +b2 = get_option('test_opt2') +test_bool = b1 or b2 +test_bool = b1 and b2 if false dependency('somethingthatdoesnotexist', required: true) @@ -11,7 +17,7 @@ endif subdir('sharedlib') subdir('staticlib') -t1 = executable('test1', 't1.cpp', link_with: [sharedlib], install: true) +t1 = executable('test1', 't1.cpp', link_with: [sharedlib], install: true, build_by_default: get_option('test_opt2')) t2 = executable('test2', 't2.cpp', link_with: [staticlib]) t3 = executable('test3', 't3.cpp', link_with: [sharedlib, staticlib], dependencies: [dep1]) diff --git a/test cases/unit/55 introspection/meson_options.txt b/test cases/unit/55 introspection/meson_options.txt new file mode 100644 index 0000000..fc5cb4d --- /dev/null +++ b/test cases/unit/55 introspection/meson_options.txt @@ -0,0 +1,2 @@ +option('test_opt1', type: 'boolean', value: false, description: 'simple boolean flag') +option('test_opt2', type: 'boolean', value: true, description: 'simple boolean flag') diff --git a/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc b/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc new file mode 100644 index 0000000..6d08687 --- /dev/null +++ b/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc @@ -0,0 +1,7 @@ +prefix=/ +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: totally_made_up_dep +Description: completely and totally made up for a test case +Version: 1.2.3
\ No newline at end of file diff --git a/test cases/unit/55 pkg_config_path option/meson.build b/test cases/unit/55 pkg_config_path option/meson.build new file mode 100644 index 0000000..623c3a2 --- /dev/null +++ b/test cases/unit/55 pkg_config_path option/meson.build @@ -0,0 +1,3 @@ +project('pkg_config_path option') + +dependency('totally_made_up_dep', method : 'pkg-config') |