diff options
33 files changed, 246 insertions, 188 deletions
@@ -21,4 +21,5 @@ packagecache /dist /meson.egg-info -hotdoc-private* +/docs/built_docs +/docs/hotdoc-private* diff --git a/cross/iphone.txt b/cross/iphone.txt index b2fe3c9..e714da5 100644 --- a/cross/iphone.txt +++ b/cross/iphone.txt @@ -20,7 +20,7 @@ has_function_printf = true has_function_hfkerhisadf = false [host_machine] -system = 'ios' +system = 'darwin' cpu_family = 'arm' cpu = 'armv7' endian = 'little' diff --git a/docs/markdown/Getting-meson.md b/docs/markdown/Getting-meson.md index c01a947..d654ff3 100644 --- a/docs/markdown/Getting-meson.md +++ b/docs/markdown/Getting-meson.md @@ -12,7 +12,7 @@ The newest development code can be obtained directly from [Git] ## Dependencies Meson is implemented in Python 3. If your operating system provides a -package manager, use should install it with that. For platforms that +package manager, you should install it with that. For platforms that don't have a package manager, you need to download it from [Python's home page]. diff --git a/docs/markdown/Overview.md b/docs/markdown/Overview.md index 537aa8f..c9acda5 100644 --- a/docs/markdown/Overview.md +++ b/docs/markdown/Overview.md @@ -6,7 +6,7 @@ short-description: Overview of the Meson build system Meson is a build system that is designed to be as user-friendly as possible without sacrificing performance. The main tool for this is a custom language that the user uses to describe the structure of his build. The main design goals of this language has been simplicity, clarity and conciseness. Much inspiration was drawn from the Python programming language, which is considered very readable, even to people who have not programmed in Python before. -Another main idea has been to provide first class support for modern programming tools and best practices. These include features as varied as unit testing, code coverage reporting, precompiled headers and the like. All of these features should be immediately available to any project using Meson. The user should not need to hunt for third party macros or write shell scripts to get these features. They just just work out of the box. +Another main idea has been to provide first class support for modern programming tools and best practices. These include features as varied as unit testing, code coverage reporting, precompiled headers and the like. All of these features should be immediately available to any project using Meson. The user should not need to hunt for third party macros or write shell scripts to get these features. They should just work out of the box. This power should not come at the expense of limited usability. Many software builds require unorthodox steps. A common example is that you first need to build a custom tool and then use that tool to generate more source code to build. This functionality needs to be supported and be as easy to use as other parts of the system. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index a6dc968..c835f6f 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -132,7 +132,7 @@ These are all the supported keyword arguments: ### custom_target() ``` meson - ctarget custom_target(*name*, ...) + customtarget custom_target(*name*, ...) ``` Create a custom top level build target. The only positional argument is the name of this target and the keyword arguments are the following. @@ -158,6 +158,8 @@ The list of strings passed to the `command` keyword argument accept the followin - `@OUTDIR@` the full path to the directory where the output(s) must be written - `@DEPFILE@` the full path to the dependency file passed to `depfile` +The returned object also has methods that are documented in the [object methods section](#custom-target-object) below. + ### declare_dependency() ``` meson @@ -212,7 +214,7 @@ Returns an empty [environment variable object](#environment-object). ### executable() ``` meson - exe executable(*exe_name*, *sources*, ...) + buildtarget executable(*exe_name*, *sources*, ...) ``` Creates a new executable. The first argument specifies its name and the remaining positional arguments define the input files to use. They can be of the following types: @@ -647,7 +649,7 @@ Defined tests can be run in a backend-agnostic way by calling `mesontest` inside ### vcs_tag() ``` meson - ctarget vcs_tag(...) + customtarget vcs_tag(...) ``` This command detects revision control commit information at build time and places it in the specified output file. This file is guaranteed to be up to date on every build. Keywords are similar to `custom_target`. @@ -737,18 +739,6 @@ When all compilation is 'native', all the methods return the same values as `bui Note that while cross-compiling, it simply returns the values defined in the cross-info file. If `target_machine` values are not defined in the cross-info file, `host_machine` values are returned instead. -### `build target` object - -A build target is either an [executable](#executable), [shared](#shared_library) or [static library](#static_library). - -- `extract_objects()` returns an opaque value representing the generated object files of arguments, usually used to take single object files and link them to unit tests or to compile some source files with custom flags. To use the object file(s) in another build target, use the `objects:` keyword argument. - -- `extract_all_objects()` is same as above but returns all object files generated by this target - -- `private_dir_include()` returns a opaque value that works like `include_directories` but points to the private directory of this target, usually only needed if an another target needs to access some generated internal headers of this target - -- `full_path()` returns a full path pointing to the result target file - ### `compiler` object This object is returned by [`meson.get_compiler(lang)`](#meson-object). It represents a compiler for a given language and allows you to query its properties. It has the following methods: @@ -833,14 +823,17 @@ You can also iterate over arrays with the [`foreach` statement](https://github.c These are objects returned by the [functions listed above](#functions). -### `run result` object +### `build target` object -This object encapsulates the result of trying to compile and run a sample piece of code with [`compiler.run()`](#compiler-object) or [`run_command()`](#run_command). It has the following methods: +A build target is either an [executable](#executable), [shared](#shared_library), [static library](#static_library) or [shared module](#shared_module). -- `compiled()` if true, the compilation succeeded, if false it did not and the other methods return unspecified data -- `returncode()` the return code of executing the compiled binary -- `stdout()` the standard out produced when the binary was run -- `stderr()` the standard error produced when the binary was run +- `extract_objects()` returns an opaque value representing the generated object files of arguments, usually used to take single object files and link them to unit tests or to compile some source files with custom flags. To use the object file(s) in another build target, use the `objects:` keyword argument. + +- `extract_all_objects()` is same as above but returns all object files generated by this target + +- `private_dir_include()` returns a opaque value that works like `include_directories` but points to the private directory of this target, usually only needed if an another target needs to access some generated internal headers of this target + +- `full_path()` returns a full path pointing to the result target file ### `configuration` data object @@ -854,6 +847,12 @@ This object is returned by [`configuration_data()`](#configuration_data) and enc They all take the `description` keyword that will be written in the result file. The replacement assumes a file with C syntax. If your generated file is source code in some other language, you probably don't want to add a description field because it most likely will cause a syntax error. +### `custom target` object + +This object is returned by [`custom_target`](#custom_target) and contains a target with the following methods: + +- `full_path()` returns a full path pointing to the result target file + ### `dependency` object This object is returned by [`dependency()`](#dependency) and contains an external dependency with the following methods: @@ -906,3 +905,11 @@ This object is returned by [`subproject()`](#subproject) and is an opaque object subproject. This is useful to, for instance, get a [declared dependency](#declare_dependency) from the subproject. +### `run result` object + +This object encapsulates the result of trying to compile and run a sample piece of code with [`compiler.run()`](#compiler-object) or [`run_command()`](#run_command). It has the following methods: + +- `compiled()` if true, the compilation succeeded, if false it did not and the other methods return unspecified data +- `returncode()` the return code of executing the compiled binary +- `stdout()` the standard out produced when the binary was run +- `stderr()` the standard error produced when the binary was run diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 1aa7806..13d66f7 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -30,7 +30,7 @@ Note how you need to specify multiple values as an array. Coverage -- -If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](https://software.sandia.gov/trac/fast/wiki/gcovr) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml). +If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml). The the output of these commands is written to the log directory `meson-logs` in your build directory. diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index 574df27..0d24d6b 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -9,6 +9,7 @@ If you have a project that uses Meson that you want to add to this list, let us - [AQEMU](https://github.com/tobimensch/aqemu), a Qt GUI for QEMU virtual machines, since version 0.9.3 - [Arduino sample project](https://github.com/jpakkane/mesonarduino) - [Emeus](https://github.com/ebassi/emeus), Constraint based layout manager for GTK+ + - [Frida.re](https://github.com/frida/frida-core), a code tracing framework - [GLib](https://github.com/centricular/glib/), cross-platform C library used by GTK+ (not merged yet) - [Gnome Builder](https://git.gnome.org/browse/gnome-builder/), an IDE for the Gnome platform - [Gnome MPV](https://github.com/gnome-mpv/gnome-mpv), Gnome frontend to the mpv video player @@ -19,6 +20,7 @@ If you have a project that uses Meson that you want to add to this list, let us - [Grilo](https://mail.gnome.org/archives/grilo-list/2017-February/msg00000.html) and [Grilo plugins](https://git.gnome.org/browse/grilo-plugins/commit/?id=ea047c4fb63e90268eb795ed91a09a2be5068a4c), the Grilo multimedia framework - [GStreamer](https://cgit.freedesktop.org/gstreamer/gstreamer/), multimedia framework (not the default yet) - [GTK+](https://git.gnome.org/browse/gtk+/log/?h=wip/meson), the multi-platform toolkit used by GNOME (not merged yet) + - [GtkDApp](https://gitlab.com/csoriano/GtkDApp), an application template for developing Flatpak apps with Gtk+ and D - [Json-glib](https://git.gnome.org/browse/json-glib), GLib-based JSON manipulation library - [Libepoxy](https://github.com/anholt/libepoxy/), a library for handling OpenGL function pointer management - [Libgit2-glib](https://git.gnome.org/browse/libgit2-glib/), a GLib wrapper for libgit2 @@ -30,7 +32,8 @@ If you have a project that uses Meson that you want to add to this list, let us - [Pitivi](http://pitivi.org/), a nonlinear video editor - [Polari](https://git.gnome.org/browse/polari), an IRC client - [Sysprof](https://wiki.gnome.org/Apps/Sysprof), a profiling tool - - [systemd](https://github.com/systemd/systemd/pull/5704), the init system (not merged yet) + - [systemd](https://github.com/systemd/systemd/pull/5704), the init system (not the default yet) + - [Xorg](https://cgit.freedesktop.org/xorg/xserver/) the X.org display server (not the default yet) - [Valum](https://github.com/valum-framework/valum), a micro web framework written in Vala - [Wayland and Weston](https://lists.freedesktop.org/archives/wayland-devel/2016-November/031984.html), a next generation display server (not merged yet) - [ZStandard](https://github.com/facebook/zstd/commit/4dca56ed832c6a88108a2484a8f8ff63d8d76d91) a compression algorithm developed at Facebook (not used by default) diff --git a/docs/markdown/tmp.json b/docs/markdown/tmp.json deleted file mode 100644 index 190d12f..0000000 --- a/docs/markdown/tmp.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "functions": { - "add_global_arguments": { - "doc": ["Adds the positional arguments to the compiler command line for the language specified in `language` keyword argument. 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.", - "The arguments are used in all compiler invocations with the exception of compile tests, because you might need to run a compile test with and without the argument in question. For this reason only the arguments explicitly specified are used during compile tests.", - "**Note:** Usually you should use `add_project_arguments` instead, because that works even when you project is used as a subproject.", - "**Note:** You must pass always arguments individually `arg1, arg2, ...` rather than as a string `'arg1 arg2', ...`"] - "arguments": [ - {"name": "args1", "doc": "Some argument"} - ] - } - } - "objects": [ - "meson": { - "doc": ["The `meson` object allows you to introspect various properties of the system. This object is always mapped in the `meson` variable. It has the following methods."] - "methods": [ - "get_compiler": { - "arguments": [ - {"name": "language", "doc": "returns [an object describing a compiler](#compiler-object), takes one positional argument which is the language to use. ", - "It also accepts one keyword argument, `native` which when set to true makes Meson return the compiler for the build machine (the \"native\" compiler)", - "and when false it returns the host compiler (the \"cross\" compiler). If `native` is omitted, Meson returns the \"cross\" compiler if we're currently", - " cross-compiling and the \"native\" compiler if we're not."} - ] - } - ] - } - ] -} diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 9dceb05..7246749 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -50,13 +50,13 @@ index.md Reproducible-builds.md howtox.md Wrap-dependency-system-manual.md - fallback-wraptool.md Adding-new-projects-to-wrapdb.md Using-the-WrapDB.md Using-wraptool.md Wrap-best-practices-and-tips.md Wrap-review-guidelines.md Shipping-prebuilt-binaries-as-wraps.md + fallback-wraptool.md Release-notes.md Release-notes-for-0.41.0.md Release-notes-for-0.40.0.md diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b903f4c..a95294b 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -529,9 +529,7 @@ class Backend: return ifilename = os.path.join(self.environment.get_build_dir(), 'depmf.json') ofilename = os.path.join(self.environment.get_prefix(), self.build.dep_manifest_name) - mfobj = {'type': 'dependency manifest', - 'version': '1.0'} - mfobj['projects'] = self.build.dep_manifest + mfobj = {'type': 'dependency manifest', 'version': '1.0', 'projects': self.build.dep_manifest} with open(ifilename, 'w') as f: f.write(json.dumps(mfobj)) # Copy file from, to, and with mode unchanged diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index de03db4..4b12da6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -21,7 +21,7 @@ from .. import dependencies from .. import compilers from ..compilers import CompilerArgs from ..mesonlib import File, MesonException, OrderedSet -from ..mesonlib import get_meson_script, get_compiler_for_source, Popen_safe +from ..mesonlib import get_meson_script, get_compiler_for_source from .backends import CleanTrees, InstallData from ..build import InvalidArguments import os, sys, pickle, re @@ -315,14 +315,14 @@ int dummy; # Now we handle the following languages: # ObjC++, ObjC, C++, C, D, Fortran, Vala - # Pre-existing target C/C++ sources to be built; dict of full path to - # source relative to build root and the original File object. - target_sources = OrderedDict() - # GeneratedList and CustomTarget sources to be built; dict of the full - # path to source relative to build root and the generating target/list - generated_sources = OrderedDict() - # Array of sources generated by valac that have to be compiled - vala_generated_sources = [] + # target_sources: + # Pre-existing target C/C++ sources to be built; dict of full path to + # source relative to build root and the original File object. + # generated_sources: + # GeneratedList and CustomTarget sources to be built; dict of the full + # path to source relative to build root and the generating target/list + # vala_generated_sources: + # Array of sources generated by valac that have to be compiled if 'vala' in target.compilers: # Sources consumed by valac are filtered out. These only contain # C/C++ sources, objects, generated libs, and unknown sources now. @@ -331,6 +331,7 @@ int dummy; else: target_sources = self.get_target_sources(target) generated_sources = self.get_target_generated_sources(target) + vala_generated_sources = [] self.scan_fortran_module_outputs(target) # Generate rules for GeneratedLists self.generate_generator_list_rules(target, outfile) diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py index b8e3504..eb543ee 100644 --- a/mesonbuild/backend/vs2015backend.py +++ b/mesonbuild/backend/vs2015backend.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from xml.etree import ElementTree as ET from .vs2010backend import Vs2010Backend diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c1e192d..855c0bd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -227,8 +227,6 @@ class ExtractedObjects: 'You can only extract all the object files at once.' raise MesonException(msg) - def get_want_all_objects(self): - return self.want_all_objects class EnvironmentVariables: def __init__(self): @@ -238,7 +236,7 @@ class EnvironmentVariables: repr_str = "<{0}: {1}>" return repr_str.format(self.__class__.__name__, self.envvars) - def get_value(self, name, values, kwargs): + def get_value(self, values, kwargs): separator = kwargs.get('separator', os.pathsep) value = '' @@ -247,16 +245,16 @@ class EnvironmentVariables: return separator, value.strip(separator) def set(self, env, name, values, kwargs): - return self.get_value(name, values, kwargs)[1] + return self.get_value(values, kwargs)[1] def append(self, env, name, values, kwargs): - sep, value = self.get_value(name, values, kwargs) + sep, value = self.get_value(values, kwargs) if name in env: return env[name] + sep + value return value def prepend(self, env, name, values, kwargs): - sep, value = self.get_value(name, values, kwargs) + sep, value = self.get_value(values, kwargs) if name in env: return value + sep + env[name] diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index fa06ae9..97a1064 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -51,8 +51,8 @@ c_suffixes = lang_suffixes['c'] + ('h',) # used in build.py:process_compilers() and build.py:get_dynamic_linker() clike_langs = ('objcpp', 'objc', 'd', 'cpp', 'c', 'fortran',) clike_suffixes = () -for l in clike_langs: - clike_suffixes += lang_suffixes[l] +for _l in clike_langs: + clike_suffixes += lang_suffixes[_l] clike_suffixes += ('h', 'll', 's') # All these are only for C-like languages; see `clike_langs` above. @@ -329,8 +329,7 @@ def build_unix_rpath_args(build_dir, rpath_paths, install_rpath): return ['-Wl,-rpath,' + paths] class CrossNoRunException(MesonException): - def __init(self, *args, **kwargs): - Exception.__init__(self, *args, **kwargs) + pass class RunResult: def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'): @@ -1018,31 +1017,31 @@ class CCompiler(Compiler): int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}''' return self.compiles(t.format(**fargs), env, extra_args, dependencies) - def cross_compute_int(self, expression, l, h, guess, prefix, env, extra_args, dependencies): + def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): if isinstance(guess, int): if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies): return guess - cur = l - while l < h: - cur = int((l + h) / 2) - if cur == l: + cur = low + while low < high: + cur = int((low + high) / 2) + if cur == low: break if self._compile_int('%s >= %d' % (expression, cur), prefix, env, extra_args, dependencies): - l = cur + low = cur else: - h = cur + high = cur if self._compile_int('%s == %d' % (expression, cur), prefix, env, extra_args, dependencies): return cur raise EnvironmentException('Cross-compile check overflowed') - def compute_int(self, expression, l, h, guess, prefix, env, extra_args=None, dependencies=None): + def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] if self.is_cross: - return self.cross_compute_int(expression, l, h, guess, prefix, env, extra_args, dependencies) + return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies) fargs = {'prefix': prefix, 'expression': expression} t = '''#include<stdio.h> {prefix} diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index f4ecd57..139ff39 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -547,6 +547,7 @@ class ExtraFrameworkDependency(Dependency): def __init__(self, name, required, path, kwargs): Dependency.__init__(self, 'extraframeworks', kwargs) self.name = None + self.required = required self.detect(name, path) if self.found(): mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'), @@ -570,6 +571,8 @@ class ExtraFrameworkDependency(Dependency): self.path = p self.name = d return + if not self.found() and self.required: + raise DependencyException('Framework dependency %s not found.' % (name, )) def get_compile_args(self): if self.found(): diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 3374c6e..3e0b558 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -95,7 +95,6 @@ class BoostDependency(Dependency): def get_compile_args(self): args = [] - include_dir = '' if self.boost_root is not None: if mesonlib.is_windows(): include_dir = self.boost_root @@ -203,7 +202,7 @@ class BoostDependency(Dependency): self.lib_modules_mt[modname] = fname def detect_lib_modules_nix(self): - if mesonlib.is_osx(): + if mesonlib.is_osx() and not self.want_cross: libsuffix = 'dylib' else: libsuffix = 'so' diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 13bbe62..bf58472 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, re, subprocess, platform -from . import coredata -from . import mesonlib -from . import mlog +import os +import platform +import re + from .compilers import * from .mesonlib import EnvironmentException, Popen_safe import configparser @@ -459,11 +459,11 @@ class Environment: for compiler in compilers: if isinstance(compiler, str): compiler = [compiler] + if 'cl' in compiler or 'cl.exe' in compiler: + arg = '/?' + else: + arg = '--version' try: - if 'cl' in compiler or 'cl.exe' in compiler: - arg = '/?' - else: - arg = '--version' p, out, err = Popen_safe(compiler + [arg]) except OSError as e: popen_exceptions[' '.join(compiler + [arg])] = e @@ -738,7 +738,7 @@ class Environment: if p.returncode == 1 and err.startswith('usage'): # OSX return ArLinker(linker) self._handle_exceptions(popen_exceptions, linkers, 'linker') - raise EnvironmentException('Unknown static linker "%s"' % ' '.join(linker)) + raise EnvironmentException('Unknown static linker "%s"' % ' '.join(linkers)) def detect_ccache(self): try: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 5df26cc..948a6d4 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -100,7 +100,7 @@ class RunProcess(InterpreterObject): try: return Popen_safe(command_array, env=child_env, cwd=cwd) except FileNotFoundError: - raise InterpreterException('Could not execute command "%s".' % cmd_name) + raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array)) def returncode_method(self, args, kwargs): return self.returncode @@ -573,6 +573,7 @@ class CustomTargetHolder(TargetHolder): class RunTargetHolder(InterpreterObject): def __init__(self, name, command, args, dependencies, subdir): + super().__init__() self.held_object = build.RunTarget(name, command, args, dependencies, subdir) def __repr__(self): @@ -1353,7 +1354,6 @@ class Interpreter(InterpreterBase): def module_method_callback(self, return_object): if not isinstance(return_object, ModuleReturnValue): - assert False raise InterpreterException('Bug in module, it returned an invalid object') invalues = return_object.new_objects self.process_new_values(invalues) @@ -2625,11 +2625,10 @@ different subdirectory. raise InterpreterException('Tried to add non-existing source file %s.' % s) def format_string(self, templ, args): - templ = self.to_native(templ) if isinstance(args, mparser.ArgumentNode): args = args.arguments for (i, arg) in enumerate(args): - arg = self.to_native(self.evaluate_statement(arg)) + arg = self.evaluate_statement(arg) if isinstance(arg, bool): # Python boolean is upper case. arg = str(arg).lower() templ = templ.replace('@{}@'.format(i), str(arg)) diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 86a6b47..fb87ea2 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -198,8 +198,6 @@ class InterpreterBase: def evaluate_notstatement(self, cur): v = self.evaluate_statement(cur.value) - if isinstance(v, mparser.BooleanNode): - v = v.value if not isinstance(v, bool): raise InterpreterException('Argument to "not" is not a boolean.') return not v @@ -217,20 +215,21 @@ class InterpreterBase: self.evaluate_codeblock(node.elseblock) def evaluate_comparison(self, node): - v1 = self.evaluate_statement(node.left) - v2 = self.evaluate_statement(node.right) - if self.is_elementary_type(v1): - val1 = v1 - else: - val1 = v1.value - if self.is_elementary_type(v2): - val2 = v2 - else: - val2 = v2.value + val1 = self.evaluate_statement(node.left) + val2 = self.evaluate_statement(node.right) if node.ctype == '==': return val1 == val2 elif node.ctype == '!=': return val1 != val2 + elif not isinstance(val1, type(val2)): + raise InterpreterException( + 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__, + type(val2).__name__, + node.ctype)) + elif not self.is_elementary_type(val1): + raise InterpreterException('{} can only be compared for equality.'.format(node.left.value)) + elif not self.is_elementary_type(val2): + raise InterpreterException('{} can only be compared for equality.'.format(node.right.value)) elif node.ctype == '<': return val1 < val2 elif node.ctype == '<=': @@ -244,45 +243,35 @@ class InterpreterBase: def evaluate_andstatement(self, cur): l = self.evaluate_statement(cur.left) - if isinstance(l, mparser.BooleanNode): - l = l.value if not isinstance(l, bool): raise InterpreterException('First argument to "and" is not a boolean.') if not l: return False r = self.evaluate_statement(cur.right) - if isinstance(r, mparser.BooleanNode): - r = r.value if not isinstance(r, bool): raise InterpreterException('Second argument to "and" is not a boolean.') return r def evaluate_orstatement(self, cur): l = self.evaluate_statement(cur.left) - if isinstance(l, mparser.BooleanNode): - l = l.get_value() if not isinstance(l, bool): raise InterpreterException('First argument to "or" is not a boolean.') if l: return True r = self.evaluate_statement(cur.right) - if isinstance(r, mparser.BooleanNode): - r = r.get_value() if not isinstance(r, bool): raise InterpreterException('Second argument to "or" is not a boolean.') return r def evaluate_uminusstatement(self, cur): v = self.evaluate_statement(cur.value) - if isinstance(v, mparser.NumberNode): - v = v.value if not isinstance(v, int): raise InterpreterException('Argument to negation is not an integer.') return -v def evaluate_arithmeticstatement(self, cur): - l = self.to_native(self.evaluate_statement(cur.left)) - r = self.to_native(self.evaluate_statement(cur.right)) + l = self.evaluate_statement(cur.left) + r = self.evaluate_statement(cur.right) if cur.operation == 'add': try: @@ -382,8 +371,6 @@ class InterpreterBase: obj = self.evaluate_statement(invokable) method_name = node.name args = node.args - if isinstance(obj, mparser.StringNode): - obj = obj.get_value() if isinstance(obj, str): return self.string_method_call(obj, method_name, args) if isinstance(obj, bool): @@ -402,7 +389,6 @@ class InterpreterBase: return obj.method_call(method_name, self.flatten(args), kwargs) def bool_method_call(self, obj, method_name, args): - obj = self.to_native(obj) (posargs, _) = self.reduce_arguments(args) if method_name == 'to_string': if not posargs: @@ -426,7 +412,6 @@ class InterpreterBase: raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) def int_method_call(self, obj, method_name, args): - obj = self.to_native(obj) (posargs, _) = self.reduce_arguments(args) if method_name == 'is_even': if not posargs: @@ -442,7 +427,6 @@ class InterpreterBase: raise InterpreterException('Unknown method "%s" for an integer.' % method_name) def string_method_call(self, obj, method_name, args): - obj = self.to_native(obj) (posargs, _) = self.reduce_arguments(args) if method_name == 'strip': return obj.strip() @@ -534,8 +518,6 @@ class InterpreterBase: raise InvalidArguments('Keyword argument name is not a string.') a = args.kwargs[key] reduced_kw[key] = self.evaluate_statement(a) - if not isinstance(reduced_pos, list): - reduced_pos = [reduced_pos] self.argument_depth -= 1 return reduced_pos, reduced_kw @@ -564,7 +546,6 @@ To specify a keyword argument, use : instead of =.''') if not isinstance(var_name, str): raise InvalidArguments('Tried to assign value to a non-variable.') value = self.evaluate_statement(node.value) - value = self.to_native(value) if not self.is_assignable(value): raise InvalidCode('Tried to assign an invalid value to variable.') # For mutable objects we need to make a copy on assignment @@ -593,12 +574,6 @@ To specify a keyword argument, use : instead of =.''') return self.variables[varname] raise InvalidCode('Unknown variable "%s".' % varname) - def to_native(self, arg): - if isinstance(arg, (mparser.StringNode, mparser.NumberNode, - mparser.BooleanNode)): - return arg.value - return arg - def is_assignable(self, value): return isinstance(value, (InterpreterObject, dependencies.Dependency, str, int, list, mesonlib.File)) @@ -624,7 +599,7 @@ To specify a keyword argument, use : instead of =.''') if len(args) != 2: raise InvalidCode('Set_variable takes two arguments.') varname = args[0] - value = self.to_native(args[1]) + value = args[1] self.set_variable(varname, value) # @noKwargs diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 585e11c..14eddf5 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -26,8 +26,7 @@ parser.add_argument('--clearcache', action='store_true', default=False, help='Clear cached state (e.g. found dependencies)') class ConfException(mesonlib.MesonException): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + pass class Conf: def __init__(self, build_dir): @@ -62,7 +61,7 @@ class Conf: len_name = longest_name = len(titles['name']) len_descr = longest_descr = len(titles['descr']) len_value = longest_value = len(titles['value']) - len_choices = longest_choices = 0 # not printed if we don't get any optional values + longest_choices = 0 # not printed if we don't get any optional values # calculate the max length of each for x in arr: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 88ea16e..525a41d 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -79,9 +79,7 @@ def list_installed(installdata): def list_targets(coredata, builddata, installdata): tlist = [] for (idname, target) in builddata.get_targets().items(): - t = {} - t['name'] = target.get_basename() - t['id'] = idname + t = {'name': target.get_basename(), 'id': idname} fname = target.get_filename() if isinstance(fname, list): fname = [os.path.join(target.subdir, x) for x in fname] @@ -132,9 +130,7 @@ def add_keys(optlist, options): keys.sort() for key in keys: opt = options[key] - optdict = {} - optdict['name'] = key - optdict['value'] = opt.value + optdict = {'name': key, 'value': opt.value} if isinstance(opt, coredata.UserStringOption): typestr = 'string' elif isinstance(opt, coredata.UserBooleanOption): @@ -190,9 +186,7 @@ def list_tests(testdata): print(json.dumps(result)) def list_projinfo(builddata): - result = {} - result['name'] = builddata.project_name - result['version'] = builddata.project_version + result = {'name': builddata.project_name, 'version': builddata.project_version} subprojects = [] for k, v in builddata.subprojects.items(): c = {'name': k, diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 134060f..b8092b2 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -730,7 +730,7 @@ class GnomeModule(ExtensionModule): return args - def gtkdoc_html_dir(self, state, args, kwarga): + def gtkdoc_html_dir(self, state, args, kwargs): if len(args) != 1: raise MesonException('Must have exactly one argument.') modulename = args[0] diff --git a/mesonbuild/scripts/delwithsuffix.py b/mesonbuild/scripts/delwithsuffix.py index bd34202..0d410ae 100644 --- a/mesonbuild/scripts/delwithsuffix.py +++ b/mesonbuild/scripts/delwithsuffix.py @@ -15,12 +15,12 @@ import os, sys def run(args): - if len(sys.argv) != 3: + if len(args) != 2: print('delwithsuffix.py <root of subdir to process> <suffix to delete>') sys.exit(1) - topdir = sys.argv[1] - suffix = sys.argv[2] + topdir = args[0] + suffix = args[1] if suffix[0] != '.': suffix = '.' + suffix diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py index ba6df7d..f17b296 100644 --- a/mesonbuild/scripts/dist.py +++ b/mesonbuild/scripts/dist.py @@ -13,9 +13,8 @@ # limitations under the License. -import os, sys +import os import shutil -import argparse import subprocess import pickle import hashlib @@ -113,7 +112,7 @@ def check_dist(packagename, meson_command): print('Installing the distribution package failed.') return 1 finally: - shutil.rmtree(srcdir) + shutil.rmtree(unpackdir) shutil.rmtree(builddir) shutil.rmtree(installdir) print('Distribution package %s tested.' % packagename) @@ -141,8 +140,7 @@ def run(args): error_count = 0 for name in names: rc = check_dist(name, meson_command) # Check only one. - rc = 0 if rc == 0: create_hash(name) error_count += rc - return rc + return 1 if error_count else 0 diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index fbcc2a4..d949090 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -34,7 +34,7 @@ def set_mode(path, mode): except PermissionError as e: msg = '{!r}: Unable to set owner {!r} and group {!r}: {}, ignoring...' print(msg.format(path, mode.owner, mode.group, e.strerror)) - except LookupError as e: + except LookupError: msg = '{!r}: Non-existent owner {!r} or group {!r}: ignoring...' print(msg.format(path, mode.owner, mode.group)) except OSError as e: diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 713d685..b17d931 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -178,7 +178,6 @@ class Resolver: if is_there: try: subprocess.check_call(['git', 'rev-parse'], cwd=checkoutdir) - is_there = True except subprocess.CalledProcessError: raise RuntimeError('%s is not empty but is not a valid ' 'git repository, we can not work with it' @@ -302,12 +301,13 @@ class Resolver: try: import lzma del lzma + except ImportError: + pass + else: try: shutil.register_unpack_format('xztar', ['.tar.xz', '.txz'], shutil._unpack_tarfile, [], "xz'ed tar-file") except shutil.RegistryError: pass - except ImportError: - pass target_dir = os.path.join(self.subdir_root, package.get('directory')) if os.path.isdir(target_dir): return diff --git a/mesontest.py b/mesontest.py index 101ce5f..9421c7f 100755 --- a/mesontest.py +++ b/mesontest.py @@ -304,7 +304,7 @@ class TestHarness: if jsonlogfile: write_json_log(jsonlogfile, name, result) - def print_summary(self, logfile, jsonlogfile): + def print_summary(self, logfile): msg = ''' OK: %4d FAIL: %4d @@ -446,7 +446,7 @@ TIMEOUT: %4d assert(isinstance(wrap, list)) return wrap - def get_pretty_suite(self, test, tests): + def get_pretty_suite(self, test): if len(self.suites) > 1: rv = TestHarness.split_suite_string(test.suite[0])[0] s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite) @@ -457,24 +457,24 @@ TIMEOUT: %4d return test.name def run_tests(self, tests): + executor = None + logfile = None + jsonlogfile = None + futures = [] try: - executor = None - logfile = None - jsonlogfile = None - futures = [] numlen = len('%d' % len(tests)) (logfile, logfilename, jsonlogfile, jsonlogfilename) = self.open_log_files() wrap = self.get_wrapper() - for i in range(self.options.repeat): + for _ in range(self.options.repeat): for i, test in enumerate(tests): - visible_name = self.get_pretty_suite(test, tests) + visible_name = self.get_pretty_suite(test) if self.options.gdb: test.timeout = None if not test.is_parallel or self.options.gdb: - self.drain_futures(futures, logfile, jsonlogfile) + self.drain_futures(futures) futures = [] res = self.run_single_test(wrap, test) self.print_stats(numlen, tests, visible_name, res, i, logfile, jsonlogfile) @@ -488,8 +488,8 @@ TIMEOUT: %4d if self.options.repeat > 1 and self.fail_count: break - self.drain_futures(futures, logfile, jsonlogfile) - self.print_summary(logfile, jsonlogfile) + self.drain_futures(futures) + self.print_summary(logfile) self.print_collected_logs() if logfilename: @@ -500,7 +500,7 @@ TIMEOUT: %4d if logfile: logfile.close() - def drain_futures(self, futures, logfile, jsonlogfile): + def drain_futures(self, futures): for i in futures: (result, numlen, tests, name, i, logfile, jsonlogfile) = i if self.options.repeat > 1 and self.fail_count: @@ -525,7 +525,7 @@ TIMEOUT: %4d def list_tests(th): tests = th.get_tests() for t in tests: - print(th.get_pretty_suite(t, tests)) + print(th.get_pretty_suite(t)) def merge_suite_options(options): buildfile = os.path.join(options.wd, 'meson-private/build.dat') @@ -558,7 +558,7 @@ def rebuild_all(wd): return False p = subprocess.Popen([ninja, '-C', wd]) - (stdo, stde) = p.communicate() + p.communicate() if p.returncode != 0: print("Could not rebuild") diff --git a/run_project_tests.py b/run_project_tests.py index ab8e3ae..7f7cfdc 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -66,9 +66,6 @@ class DummyFuture(conc.Future): ask for the result. Used on platforms where sem_open() is not available: MSYS2, OpenBSD, etc: https://bugs.python.org/issue3770 ''' - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def set_function(self, fn, *args, **kwargs): self.fn = fn self.fn_args = args @@ -220,7 +217,6 @@ def validate_install(srcdir, installdir, compiler): # If this exists, the test does not install any other files noinst_file = 'usr/no-installed-files' expected = {} - found = {} ret_msg = '' # Generate list of expected files if os.path.exists(os.path.join(installdir, noinst_file)): diff --git a/run_unittests.py b/run_unittests.py index ec9d53b..85474e8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -18,7 +18,10 @@ import shlex import subprocess import re, json import tempfile -import unittest, os, sys, shutil, time +import os +import shutil +import sys +import unittest from glob import glob from pathlib import PurePath import mesonbuild.compilers @@ -247,6 +250,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(substfunc(cmd, d), inputs + cmd[2:]) # Many inputs, can't use @INPUT@ like this cmd = ['@INPUT@.out', 'ordinary', 'strings'] + self.assertRaises(ME, substfunc, cmd, d) # Not enough inputs cmd = ['@INPUT2@.out', 'ordinary', 'strings'] self.assertRaises(ME, substfunc, cmd, d) @@ -281,6 +285,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:]) # Many inputs, can't use @INPUT@ like this cmd = ['@INPUT@.out', 'ordinary', 'strings'] + self.assertRaises(ME, substfunc, cmd, d) # Not enough inputs cmd = ['@INPUT2@.out', 'ordinary', 'strings'] self.assertRaises(ME, substfunc, cmd, d) @@ -307,6 +312,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok', 'dir']) # Many inputs, can't use @INPUT@ like this cmd = ['@INPUT@.out', 'ordinary', 'strings'] + self.assertRaises(ME, substfunc, cmd, d) # Not enough inputs cmd = ['@INPUT2@.out', 'ordinary', 'strings'] self.assertRaises(ME, substfunc, cmd, d) @@ -381,6 +387,8 @@ class BasePlatformTests(unittest.TestCase): output = p.communicate()[0] print(output) if p.returncode != 0: + if 'MESON_SKIP_TEST' in output: + raise unittest.SkipTest('Project requested skipping.') raise subprocess.CalledProcessError(p.returncode, command) return output @@ -396,6 +404,8 @@ class BasePlatformTests(unittest.TestCase): '--libdir', self.libdir] try: self._run(self.meson_command + args + extra_args) + except unittest.SkipTest: + raise unittest.SkipTest('Project requested skipping: ' + srcdir) except: self._print_meson_log() raise @@ -849,7 +859,6 @@ class AllPlatformTests(BasePlatformTests): env = Environment(testdir, self.builddir, self.meson_command, get_fake_options(self.prefix), []) for lang, evar in langs: - evalue = None # Detect with evar and do sanity checks on that if evar in os.environ: ecc = getattr(env, 'detect_{}_compiler'.format(lang))(False) diff --git a/test cases/common/19 comparison/meson.build b/test cases/common/19 comparison/meson.build index 290c7b4..c4cff9f 100644 --- a/test cases/common/19 comparison/meson.build +++ b/test cases/common/19 comparison/meson.build @@ -1,5 +1,7 @@ project('comparison', 'c') +# Compare equality of strings + var1 = 'foo' var2 = 'bar' @@ -31,3 +33,96 @@ test('equalfalse', exe1) test('equaltrue', exe2) test('nequaltrue', exe3) test('nequalfalse', exe4) + +# Non-equality comparisons + +var3 = 3 +var4 = 4 + +if var3 < var4 + exe5 = executable('prog5', 'prog.c') +else + exe5 = executable('broken', 'broken.c') +endif + +if var3 < var3 + exe6 = executable('broken', 'broken.c') +else + exe6 = executable('prog6', 'prog.c') +endif + +if var4 > var3 + exe7 = executable('prog7', 'prog.c') +else + exe7 = executable('broken', 'broken.c') +endif + +if var3 > var3 + exe8 = executable('broken', 'broken.c') +else + exe8 = executable('prog8', 'prog.c') +endif + +if var4 <= var3 + exe9 = executable('broken', 'broken.c') +else + exe9 = executable('prog9', 'prog.c') +endif + +if var3 <= var3 + exe10 = executable('prog10', 'prog.c') +else + exe10 = executable('broken', 'broken.c') +endif + +if var3 >= var4 + exe11 = executable('broken', 'broken.c') +else + exe11 = executable('prog11', 'prog.c') +endif + +if var3 >= var3 + exe12 = executable('prog12', 'prog.c') +else + exe12 = executable('broken', 'broken.c') +endif + +test('lttrue', exe5) +test('ltfalse', exe6) +test('gttrue', exe7) +test('gtfalse', exe8) +test('lefalse', exe9) +test('letrue', exe10) +test('gefalse', exe11) +test('getrue', exe12) + +# Non-elementary type comparisons + +if exe1 == exe2 + exe13 = executable('broken', 'broken.c') +else + exe13 = executable('prog13', 'prog.c') +endif + +if exe1 == exe1 + exe14 = executable('prog14', 'prog.c') +else + exe14 = executable('broken', 'broken.c') +endif + +if exe1 != exe2 + exe15 = executable('prog15', 'prog.c') +else + exe15 = executable('broken', 'broken.c') +endif + +if exe1 != exe1 + exe16 = executable('broken', 'broken.c') +else + exe16 = executable('prog16', 'prog.c') +endif + +test('equalfalse', exe13) +test('equaltrue', exe14) +test('nequaltrue', exe15) +test('nequalfalse', exe16) diff --git a/test cases/failing/51 executable comparison/meson.build b/test cases/failing/51 executable comparison/meson.build new file mode 100644 index 0000000..041bcf3 --- /dev/null +++ b/test cases/failing/51 executable comparison/meson.build @@ -0,0 +1,6 @@ +project('executable comparison', 'c') + +exe1 = executable('prog1', sources : 'prog.c') +exe2 = executable('prog2', sources : 'prog.c') + +assert(exe1 < exe2, 'should fail') diff --git a/test cases/failing/51 executable comparison/prog.c b/test cases/failing/51 executable comparison/prog.c new file mode 100644 index 0000000..0314ff1 --- /dev/null +++ b/test cases/failing/51 executable comparison/prog.c @@ -0,0 +1 @@ +int main(int argc, char **argv) { return 0; } diff --git a/test cases/failing/52 inconsistent comparison/meson.build b/test cases/failing/52 inconsistent comparison/meson.build new file mode 100644 index 0000000..7694c2c --- /dev/null +++ b/test cases/failing/52 inconsistent comparison/meson.build @@ -0,0 +1,7 @@ +project('kwarg before arg', 'c') + +# All of these should fail, though only the first one will error out if +# everything's working correctly. +assert([] < 'st', 'should fail') +assert([] < 1, 'should fail') +assert(2 < 'st', 'should fail') |