diff options
22 files changed, 363 insertions, 176 deletions
diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt index 6246ffe..fec8ce7 100644 --- a/cross/ubuntu-armhf.txt +++ b/cross/ubuntu-armhf.txt @@ -1,8 +1,8 @@ [binaries] # we could set exe_wrapper = qemu-arm-static but to test the case # when cross compiled binaries can't be run we don't do that -c = '/usr/bin/arm-linux-gnueabihf-gcc-7' -cpp = '/usr/bin/arm-linux-gnueabihf-g++-7' +c = '/usr/bin/arm-linux-gnueabihf-gcc' +cpp = '/usr/bin/arm-linux-gnueabihf-g++' rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] ar = '/usr/arm-linux-gnueabihf/bin/ar' strip = '/usr/arm-linux-gnueabihf/bin/strip' diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index e739e37..7d316ed 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -10,17 +10,23 @@ nomenclature. The three most important definitions are traditionally called *build*, *host* and *target*. This is confusing because those terms are used for quite many different things. To simplify the issue, we are going to call these the *build machine*, *host machine* and -*target machine*. Their definitions are the following +*target machine*. Their definitions are the following: -* *build machine* is the computer that is doing the actual compiling -* *host machine* is the machine on which the compiled binary will run -* *target machine* is the machine on which the compiled binary's output will run (this is only meaningful for programs such as compilers that, when run, produce object code for a different CPU than what the program is being run on) +* *build machine* is the computer that is doing the actual compiling. +* *host machine* is the machine on which the compiled binary will run. +* *target machine* is the machine on which the compiled binary's + output will run, *only meaningful* if the program produces + machine-specific output. The `tl/dr` summary is the following: if you are doing regular cross -compilation, you only care about *build_machine* and -*host_machine*. Just ignore *target_machine* altogether and you will -be correct 99% of the time. If your needs are more complex or you are -interested in the actual details, do read on. +compilation, you only care about `build_machine` and +`host_machine`. Just ignore `target_machine` altogether and you will +be correct 99% of the time. Only compilers and similar tools care +about the target machine. In fact, for so-called "multi-target" tools +the target machine need not be fixed at build-time like the others but +chosen at runtime, so `target_machine` *still* doesn't matter. If your +needs are more complex or you are interested in the actual details, do +read on. This might be easier to understand through examples. Let's start with the regular, not cross-compiling case. In these cases all of these @@ -50,6 +56,20 @@ Wikipedia or the net in general. It is very common for them to get build, host and target mixed up, even in consecutive sentences, which can leave you puzzled until you figure it out. +A lot of confusion stems from the fact that when you cross-compile +something, the 3 systems (*build*, *host*, and *target*) used when +building the cross compiler don't align with the ones used when +building something with that newly-built cross compiler. To take our +Canadian Cross scenario from above (for full generality), since its +*host machine* is x86 Windows, the *build machine* of anything we +build with it is *x86 Windows*. And since its *target machine* is MIPS +Linux, the *host machine* of anything we build with it is *MIPS +Linux*. Only the *target machine* of whatever we build with it can be +freely chosen by us, say if we want to build another cross compiler +that runs on MIPS Linux and targets Aarch64 iOS. As this example +hopefully makes clear to you, the platforms are shifted over to the +left by one position. + If you did not understand all of the details, don't worry. For most people it takes a while to wrap their head around these concepts. Don't panic, it might take a while to click, but you will @@ -82,8 +102,9 @@ of a wrapper, these lines are all you need to write. Meson will automatically use the given wrapper when it needs to run host binaries. This happens e.g. when running the project's test suite. -The next section lists properties of the cross compiler and thus of -the host system. It looks like this: +The next section lists properties of the cross compiler and its target +system, and thus properties of host system of what we're building. It +looks like this: ```ini [properties] diff --git a/docs/markdown/Pkg-config-files.md b/docs/markdown/Pkg-config-files.md index dde4ac9..ddb8bab 100644 --- a/docs/markdown/Pkg-config-files.md +++ b/docs/markdown/Pkg-config-files.md @@ -1,6 +1,6 @@ # Pkg config files -[Pkg-config](https://en.wikipedia.org/wiki/Pkg-config) is a way for shared libraries to declare the compiler flags needed to use them. There are two different ways of generating Pkg-config files in Meson. The first way is to build them manually with the `configure_files` command. The second way is to use Meson's built in Pkg-config file generator. The difference between the two is that the latter is very simple and meant for basic use cases. The former should be used when you need to provide a more customized solution. +[Pkg-config](https://en.wikipedia.org/wiki/Pkg-config) is a way for shared libraries to declare the compiler flags needed to use them. There are two different ways of generating Pkg-config files in Meson. The first way is to build them manually with the `configure_file` command. The second way is to use Meson's built in Pkg-config file generator. The difference between the two is that the latter is very simple and meant for basic use cases. The former should be used when you need to provide a more customized solution. In this document we describe the simple generator approach. It is used in the following way. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 54b7131..5109b25 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1129,6 +1129,33 @@ This function has one keyword argument. recurse in the subdir if they all return `true` when queried with `.found()` +### subdir_done() + +``` meson + subdir_done() +``` + +Stops further interpretation of the meson script file from the point of +the invocation. All steps executed up to this point are valid and will +be executed by meson. This means that all targets defined before the call +of `subdir_done` will be build. + +If the current script was called by `subdir` the execution returns to the +calling directory and continues as if the script had reached the end. +If the current script is the top level script meson configures the project +as defined up to this point. + +Example: +```meson +project('example exit', 'cpp') +executable('exe1', 'exe1.cpp') +subdir_done() +executable('exe2', 'exe2.cpp') +``` + +The executable `exe1` will be build, while the executable `exe2` is not +build. + ### subproject() ``` meson diff --git a/docs/markdown/Release-notes-for-0.46.0.md b/docs/markdown/Release-notes-for-0.46.0.md index 395a94d..e062459 100644 --- a/docs/markdown/Release-notes-for-0.46.0.md +++ b/docs/markdown/Release-notes-for-0.46.0.md @@ -14,3 +14,10 @@ whose contents should look like this: ## Feature name A short description explaining the new feature and how it should be used. + +## Allow early return from a script + +Added the function `subdir_done()`. Its invocation exits the current script at +the point of invocation. All previously invoked build targets and commands are +build/executed. All following ones are ignored. If the current script was +invoked via `subdir()` the parent script continues normally. diff --git a/docs/markdown/Users.md b/docs/markdown/Users.md index e152555..558378c 100644 --- a/docs/markdown/Users.md +++ b/docs/markdown/Users.md @@ -4,7 +4,8 @@ title: Users # List of projects using Meson -If you have a project that uses Meson that you want to add to this list, please [file a pull-request](https://github.com/mesonbuild/meson/edit/master/docs/markdown/Users.md) for it. All the software on this list is tested for regressions before release, so it's highly recommended that projects add themselves here. +If you have a project that uses Meson that you want to add to this list, please [file a pull-request](https://github.com/mesonbuild/meson/edit/master/docs/markdown/Users.md) for it. All the software on this list is tested for regressions before release, so it's highly recommended that projects add themselves here. Some additional projects are +listed in the [`meson` GitHub topic](https://github.com/topics/meson). - [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) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 3ff68ed..aca592e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -380,6 +380,7 @@ class BuildTarget(Target): self.process_compilers_late() self.validate_sources() self.validate_cross_install(environment) + self.check_module_linking() def __lt__(self, other): return self.get_id() < other.get_id() @@ -1027,6 +1028,15 @@ You probably should put it in link_with instead.''') def is_linkable_target(self): return False + def check_module_linking(self): + ''' + Warn if shared modules are linked with target: (link_with) #2865 + ''' + for link_target in self.link_targets: + if isinstance(link_target, SharedModule): + mlog.warning('''target links against shared modules. This is not +recommended as it can lead to undefined behaviour on some platforms''') + return class Generator: def __init__(self, args, kwargs): diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index ff7c706..0115fb3 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -674,7 +674,7 @@ class Environment: except OSError: raise EnvironmentException('Could not execute Java compiler "%s"' % ' '.join(exelist)) version = search_version(err) - if 'javac' in err: + if 'javac' in out or 'javac' in err: return JavaCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c87a49b..55320d0 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -26,7 +26,7 @@ from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs, permittedMethodKwargs -from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode +from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler from .modules import ModuleReturnValue @@ -1612,6 +1612,7 @@ class Interpreter(InterpreterBase): 'static_library': self.func_static_lib, 'test': self.func_test, 'vcs_tag': self.func_vcs_tag, + 'subdir_done': self.func_subdir_done, }) if 'MESON_UNIT_TEST' in os.environ: self.funcs.update({'exception': self.func_exception}) @@ -2310,7 +2311,10 @@ to directly access options of other subprojects.''') return progobj def func_find_library(self, node, args, kwargs): - raise InvalidCode('find_library() is removed, use the corresponding method in a compiler object instead.') + raise InvalidCode('find_library() is removed, use meson.get_compiler(\'name\').find_library() instead.\n' + 'Look here for documentation: http://mesonbuild.com/Reference-manual.html#compiler-object\n' + 'Look here for example: http://mesonbuild.com/howtox.html#add-math-library-lm-portably\n' + ) def _find_cached_dep(self, name, kwargs): # Check if we want this as a cross-dep or a native-dep @@ -2607,6 +2611,14 @@ root and issuing %s. return self.func_custom_target(node, [kwargs['output']], kwargs) @stringArgs + def func_subdir_done(self, node, args, kwargs): + if len(kwargs) > 0: + raise InterpreterException('exit does not take named arguments') + if len(args) > 0: + raise InterpreterException('exit does not take any arguments') + raise SubdirDoneRequest() + + @stringArgs @permittedKwargs(permitted_kwargs['custom_target']) def func_custom_target(self, node, args, kwargs): if len(args) != 1: diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 9279506..f957d90 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -105,6 +105,9 @@ class InvalidCode(InterpreterException): class InvalidArguments(InterpreterException): pass +class SubdirDoneRequest(BaseException): + pass + class InterpreterObject: def __init__(self): self.methods = {} @@ -203,6 +206,8 @@ class InterpreterBase: try: self.current_lineno = cur.lineno self.evaluate_statement(cur) + except SubdirDoneRequest: + break except Exception as e: if not(hasattr(e, 'lineno')): e.lineno = cur.lineno diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index e055616..ef74d63 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -349,7 +349,7 @@ class PkgConfigModule(ExtensionModule): if dversions: compiler = state.environment.coredata.compilers.get('d') if compiler: - deps.add_cflags(compiler.get_feature_args({'versions': dversions})) + deps.add_cflags(compiler.get_feature_args({'versions': dversions}, None)) def parse_variable_list(stringlist): reserved = ['prefix', 'libdir', 'includedir'] diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 3494b54..91567f2 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -29,6 +29,7 @@ import platform import signal import random from copy import deepcopy +import enum # GNU autotools interprets a return code of 77 from tests it executes to # mean that the test should be skipped. @@ -108,9 +109,19 @@ class TestException(mesonlib.MesonException): pass +@enum.unique +class TestResult(enum.Enum): + + OK = 'OK' + TIMEOUT = 'TIMEOUT' + SKIP = 'SKIP' + FAIL = 'FAIL' + + class TestRun: def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd, env): + assert isinstance(res, TestResult) self.res = res self.returncode = returncode self.duration = duration @@ -125,7 +136,7 @@ class TestRun: if self.cmd is None: res += 'NONE\n' else: - res += "%s%s\n" % (''.join(["%s='%s' " % (k, v) for k, v in self.env.items()]), ' ' .join(self.cmd)) + res += '%s%s\n' % (''.join(["%s='%s' " % (k, v) for k, v in self.env.items()]), ' ' .join(self.cmd)) if self.stdo: res += '--- stdout ---\n' res += self.stdo @@ -150,7 +161,7 @@ def decode(stream): def write_json_log(jsonlogfile, test_name, result): jresult = {'name': test_name, 'stdout': result.stdo, - 'result': result.res, + 'result': result.res.value, 'duration': result.duration, 'returncode': result.returncode, 'command': result.cmd} @@ -183,6 +194,139 @@ def load_tests(build_dir): obj = pickle.load(f) return obj + +class SingleTestRunner: + + def __init__(self, test, env, options): + self.test = test + self.env = env + self.options = options + + def _get_cmd(self): + if self.test.fname[0].endswith('.jar'): + return ['java', '-jar'] + self.test.fname + elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]): + return ['mono'] + self.test.fname + else: + if self.test.is_cross_built: + if self.test.exe_runner is None: + # Can not run test on cross compiled executable + # because there is no execute wrapper. + return None + else: + return [self.test.exe_runner] + self.test.fname + else: + return self.test.fname + + def run(self): + cmd = self._get_cmd() + if cmd is None: + skip_stdout = 'Not run because can not execute cross compiled binaries.' + return TestRun(res=TestResult.SKIP, returncode=GNU_SKIP_RETURNCODE, + should_fail=self.test.should_fail, duration=0.0, + stdo=skip_stdout, stde=None, cmd=None, env=self.test.env) + else: + wrap = TestHarness.get_wrapper(self.options) + if self.options.gdb: + self.test.timeout = None + return self._run_cmd(wrap + cmd + self.test.cmd_args + self.options.test_args) + + def _run_cmd(self, cmd): + starttime = time.time() + + if len(self.test.extra_paths) > 0: + self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] + + # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, + # (i.e., the test or the environment don't explicitly set it), set + # it ourselves. We do this unconditionally for regular tests + # because it is extremely useful to have. + # Setting MALLOC_PERTURB_="0" will completely disable this feature. + if ('MALLOC_PERTURB_' not in self.env or not self.env['MALLOC_PERTURB_']) and not self.options.benchmark: + self.env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) + + stdout = None + stderr = None + if not self.options.verbose: + stdout = subprocess.PIPE + stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT + + # Let gdb handle ^C instead of us + if self.options.gdb: + previous_sigint_handler = signal.getsignal(signal.SIGINT) + # Make the meson executable ignore SIGINT while gdb is running. + signal.signal(signal.SIGINT, signal.SIG_IGN) + + def preexec_fn(): + if self.options.gdb: + # Restore the SIGINT handler for the child process to + # ensure it can handle it. + signal.signal(signal.SIGINT, signal.SIG_DFL) + else: + # We don't want setsid() in gdb because gdb needs the + # terminal in order to handle ^C and not show tcsetpgrp() + # errors avoid not being able to use the terminal. + os.setsid() + + p = subprocess.Popen(cmd, + stdout=stdout, + stderr=stderr, + env=self.env, + cwd=self.test.workdir, + preexec_fn=preexec_fn if not is_windows() else None) + timed_out = False + kill_test = False + if self.test.timeout is None: + timeout = None + elif self.options.timeout_multiplier is not None: + timeout = self.test.timeout * self.options.timeout_multiplier + else: + timeout = self.test.timeout + try: + (stdo, stde) = p.communicate(timeout=timeout) + except subprocess.TimeoutExpired: + if self.options.verbose: + print('%s time out (After %d seconds)' % (self.test.name, timeout)) + timed_out = True + except KeyboardInterrupt: + mlog.warning('CTRL-C detected while running %s' % (self.test.name)) + kill_test = True + finally: + if self.options.gdb: + # Let us accept ^C again + signal.signal(signal.SIGINT, previous_sigint_handler) + + if kill_test or timed_out: + # Python does not provide multiplatform support for + # killing a process and all its children so we need + # to roll our own. + if is_windows(): + subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) + else: + try: + os.killpg(os.getpgid(p.pid), signal.SIGKILL) + except ProcessLookupError: + # Sometimes (e.g. with Wine) this happens. + # There's nothing we can do (maybe the process + # already died) so carry on. + pass + (stdo, stde) = p.communicate() + endtime = time.time() + duration = endtime - starttime + stdo = decode(stdo) + if stde: + stde = decode(stde) + if timed_out: + res = TestResult.TIMEOUT + elif p.returncode == GNU_SKIP_RETURNCODE: + res = TestResult.SKIP + elif self.test.should_fail == bool(p.returncode): + res = TestResult.OK + else: + res = TestResult.FAIL + return TestRun(res, p.returncode, self.test.should_fail, duration, stdo, stde, cmd, self.test.env) + + class TestHarness: def __init__(self, options): self.options = options @@ -210,7 +354,7 @@ class TestHarness: self.jsonlogfile.close() def merge_suite_options(self, options, test): - if ":" in options.setup: + if ':' in options.setup: if options.setup not in self.build_data.test_setups: sys.exit("Unknown test setup '%s'." % options.setup) current = self.build_data.test_setups[options.setup] @@ -231,7 +375,8 @@ class TestHarness: options.wrapper = current.exe_wrapper return current.env.get_env(os.environ.copy()) - def get_test_env(self, options, test): + def get_test_runner(self, test): + options = deepcopy(self.options) if options.setup: env = self.merge_suite_options(options, test) else: @@ -239,153 +384,33 @@ class TestHarness: if isinstance(test.env, build.EnvironmentVariables): test.env = test.env.get_env(env) env.update(test.env) - return env - - def run_single_test(self, test): - if test.fname[0].endswith('.jar'): - cmd = ['java', '-jar'] + test.fname - elif not test.is_cross_built and run_with_mono(test.fname[0]): - cmd = ['mono'] + test.fname + return SingleTestRunner(test, env, options) + + def process_test_result(self, result): + if result.res is TestResult.TIMEOUT: + self.timeout_count += 1 + self.fail_count += 1 + elif result.res is TestResult.SKIP: + self.skip_count += 1 + elif result.res is TestResult.OK: + self.success_count += 1 + elif result.res is TestResult.FAIL: + self.fail_count += 1 else: - if test.is_cross_built: - if test.exe_runner is None: - # Can not run test on cross compiled executable - # because there is no execute wrapper. - cmd = None - else: - cmd = [test.exe_runner] + test.fname - else: - cmd = test.fname - - if cmd is None: - res = 'SKIP' - duration = 0.0 - stdo = 'Not run because can not execute cross compiled binaries.' - stde = None - returncode = GNU_SKIP_RETURNCODE - else: - test_opts = deepcopy(self.options) - test_env = self.get_test_env(test_opts, test) - wrap = self.get_wrapper(test_opts) - - if test_opts.gdb: - test.timeout = None - - cmd = wrap + cmd + test.cmd_args + self.options.test_args - starttime = time.time() - - if len(test.extra_paths) > 0: - test_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + test_env['PATH'] - - # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, - # (i.e., the test or the environment don't explicitly set it), set - # it ourselves. We do this unconditionally for regular tests - # because it is extremely useful to have. - # Setting MALLOC_PERTURB_="0" will completely disable this feature. - if ('MALLOC_PERTURB_' not in test_env or not test_env['MALLOC_PERTURB_']) and not self.options.benchmark: - test_env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) - - stdout = None - stderr = None - if not self.options.verbose: - stdout = subprocess.PIPE - stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT - - # Let gdb handle ^C instead of us - if test_opts.gdb: - previous_sigint_handler = signal.getsignal(signal.SIGINT) - # Make the meson executable ignore SIGINT while gdb is running. - signal.signal(signal.SIGINT, signal.SIG_IGN) - - def preexec_fn(): - if test_opts.gdb: - # Restore the SIGINT handler for the child process to - # ensure it can handle it. - signal.signal(signal.SIGINT, signal.SIG_DFL) - else: - # We don't want setsid() in gdb because gdb needs the - # terminal in order to handle ^C and not show tcsetpgrp() - # errors avoid not being able to use the terminal. - os.setsid() - - p = subprocess.Popen(cmd, - stdout=stdout, - stderr=stderr, - env=test_env, - cwd=test.workdir, - preexec_fn=preexec_fn if not is_windows() else None) - timed_out = False - kill_test = False - if test.timeout is None: - timeout = None - elif test_opts.timeout_multiplier is not None: - timeout = test.timeout * test_opts.timeout_multiplier - else: - timeout = test.timeout - try: - (stdo, stde) = p.communicate(timeout=timeout) - except subprocess.TimeoutExpired: - if self.options.verbose: - print("%s time out (After %d seconds)" % (test.name, timeout)) - timed_out = True - except KeyboardInterrupt: - mlog.warning("CTRL-C detected while running %s" % (test.name)) - kill_test = True - finally: - if test_opts.gdb: - # Let us accept ^C again - signal.signal(signal.SIGINT, previous_sigint_handler) - - if kill_test or timed_out: - # Python does not provide multiplatform support for - # killing a process and all its children so we need - # to roll our own. - if is_windows(): - subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) - else: - try: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - except ProcessLookupError: - # Sometimes (e.g. with Wine) this happens. - # There's nothing we can do (maybe the process - # already died) so carry on. - pass - (stdo, stde) = p.communicate() - endtime = time.time() - duration = endtime - starttime - stdo = decode(stdo) - if stde: - stde = decode(stde) - if timed_out: - res = 'TIMEOUT' - self.timeout_count += 1 - self.fail_count += 1 - elif p.returncode == GNU_SKIP_RETURNCODE: - res = 'SKIP' - self.skip_count += 1 - elif test.should_fail == bool(p.returncode): - res = 'OK' - self.success_count += 1 - else: - res = 'FAIL' - self.fail_count += 1 - returncode = p.returncode - result = TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd, test.env) - - return result + sys.exit('Unknown test result encountered: {}'.format(result.res)) def print_stats(self, numlen, tests, name, result, i): startpad = ' ' * (numlen - len('%d' % (i + 1))) num = '%s%d/%d' % (startpad, i + 1, len(tests)) padding1 = ' ' * (38 - len(name)) - padding2 = ' ' * (8 - len(result.res)) + padding2 = ' ' * (8 - len(result.res.value)) result_str = '%s %s %s%s%s%5.2f s' % \ - (num, name, padding1, result.res, padding2, result.duration) - if not self.options.quiet or result.res != 'OK': - if result.res != 'OK' and mlog.colorize_console: - if result.res == 'FAIL' or result.res == 'TIMEOUT': + (num, name, padding1, result.res.value, padding2, result.duration) + if not self.options.quiet or result.res is not TestResult.OK: + if result.res is not TestResult.OK and mlog.colorize_console: + if result.res is TestResult.FAIL or result.res is TestResult.TIMEOUT: decorator = mlog.red - elif result.res == 'SKIP': + elif result.res is TestResult.SKIP: decorator = mlog.yellow else: sys.exit('Unreachable code was ... well ... reached.') @@ -517,7 +542,8 @@ TIMEOUT: %4d self.logfile.write('Log of Meson test suite run on %s\n\n' % datetime.datetime.now().isoformat()) - def get_wrapper(self, options): + @staticmethod + def get_wrapper(options): wrap = [] if options.gdb: wrap = ['gdb', '--quiet', '--nh'] @@ -558,12 +584,15 @@ TIMEOUT: %4d if not test.is_parallel or self.options.gdb: self.drain_futures(futures) futures = [] - res = self.run_single_test(test) + single_test = self.get_test_runner(test) + res = single_test.run() + self.process_test_result(res) self.print_stats(numlen, tests, visible_name, res, i) else: if not executor: executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) - f = executor.submit(self.run_single_test, test) + single_test = self.get_test_runner(test) + f = executor.submit(single_test.run) futures.append((f, numlen, tests, visible_name, i)) if self.options.repeat > 1 and self.fail_count: break @@ -586,10 +615,11 @@ TIMEOUT: %4d result.cancel() if self.options.verbose: result.result() + self.process_test_result(result.result()) self.print_stats(numlen, tests, name, result.result(), i) def run_special(self): - 'Tests run by the user, usually something like "under gdb 1000 times".' + '''Tests run by the user, usually something like "under gdb 1000 times".''' if self.is_run: raise RuntimeError('Can not use run_special after a full run.') tests = self.get_tests() @@ -606,7 +636,7 @@ def list_tests(th): def rebuild_all(wd): if not os.path.isfile(os.path.join(wd, 'build.ninja')): - print("Only ninja backend is supported to rebuild tests before running them.") + print('Only ninja backend is supported to rebuild tests before running them.') return True ninja = environment.detect_ninja() @@ -618,7 +648,7 @@ def rebuild_all(wd): p.communicate() if p.returncode != 0: - print("Could not rebuild") + print('Could not rebuild') return False return True @@ -647,7 +677,7 @@ def run(args): if check_bin is not None: exe = ExternalProgram(check_bin, silent=True) if not exe.found(): - sys.exit("Could not find requested program: %s" % check_bin) + sys.exit('Could not find requested program: %s' % check_bin) options.wd = os.path.abspath(options.wd) if not options.list and not options.no_rebuild: diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py index 2a5ee8b..3fe7fb7 100644 --- a/mesonbuild/scripts/gtkdochelper.py +++ b/mesonbuild/scripts/gtkdochelper.py @@ -58,6 +58,8 @@ def gtkdoc_run_check(cmd, cwd, library_path=None): if out: err_msg.append(out) raise MesonException('\n'.join(err_msg)) + elif out: + print(out) def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs, main_file, module, diff --git a/run_tests.py b/run_tests.py index 1cc3983..648e6ce 100755 --- a/run_tests.py +++ b/run_tests.py @@ -131,7 +131,7 @@ def get_fake_options(prefix): return opts def should_run_linux_cross_tests(): - return shutil.which('arm-linux-gnueabihf-gcc-7') and not platform.machine().lower().startswith('arm') + return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') def run_configure_inprocess(meson_command, commandlist): old_stdout = sys.stdout diff --git a/run_unittests.py b/run_unittests.py index 6ab549c..9f0ae3f 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -524,16 +524,18 @@ class BasePlatformTests(unittest.TestCase): Run a command while printing the stdout and stderr to stdout, and also return a copy of it ''' - p = subprocess.Popen(command, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, env=os.environ.copy(), - universal_newlines=True, cwd=workdir) - output = p.communicate()[0] - print(output) + # If this call hangs CI will just abort. It is very hard to distinguish + # between CI issue and test bug in that case. Set timeout and fail loud + # instead. + p = subprocess.run(command, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, env=os.environ.copy(), + universal_newlines=True, cwd=workdir, timeout=60 * 5) + print(p.stdout) if p.returncode != 0: - if 'MESON_SKIP_TEST' in output: + if 'MESON_SKIP_TEST' in p.stdout: raise unittest.SkipTest('Project requested skipping.') raise subprocess.CalledProcessError(p.returncode, command) - return output + return p.stdout def init(self, srcdir, extra_args=None, default_args=True, inprocess=False): self.assertPathExists(srcdir) @@ -1897,6 +1899,16 @@ int main(int argc, char **argv) { exception_raised = True self.assertTrue(exception_raised, 'Double locking did not raise exception.') + def test_check_module_linking(self): + """ + Test that shared modules are not linked with targets(link_with:) #2865 + """ + tdir = os.path.join(self.unit_test_dir, '26 shared_mod linking') + out = self.init(tdir) + msg = ('''WARNING: target links against shared modules. This is not +recommended as it can lead to undefined behaviour on some platforms''') + self.assertIn(msg, out) + def test_ndebug_if_release_disabled(self): testdir = os.path.join(self.unit_test_dir, '25 ndebug if-release') self.init(testdir, extra_args=['--buildtype=release', '-Db_ndebug=if-release']) diff --git a/test cases/common/188 subdir_done/meson.build b/test cases/common/188 subdir_done/meson.build new file mode 100644 index 0000000..5692f3a --- /dev/null +++ b/test cases/common/188 subdir_done/meson.build @@ -0,0 +1,10 @@ +# Should run, even though main.cpp does not exist and we call error in the last line. +# subdir_done jumps to end, so both lines are not executed. + +project('example exit', 'cpp') + +subdir_done() + +executable('main', 'main.cpp') +error('Unreachable') + diff --git a/test cases/d/3 shared library/meson.build b/test cases/d/3 shared library/meson.build index 78ad766..4616242 100644 --- a/test cases/d/3 shared library/meson.build +++ b/test cases/d/3 shared library/meson.build @@ -10,3 +10,12 @@ endif ldyn = shared_library('stuff', 'libstuff.d', install : true) ed = executable('app_d', 'app.d', link_with : ldyn, install : true) test('linktest_dyn', ed) + +# test D attributes for pkg-config +pkgc = import('pkgconfig') +pkgc.generate(name: 'test', + libraries: ldyn, + subdirs: 'd/stuff', + description: 'A test of D attributes to pkgconfig.generate.', + d_module_versions: ['Use_Static'] +) diff --git a/test cases/failing/71 skip only subdir/meson.build b/test cases/failing/71 skip only subdir/meson.build new file mode 100644 index 0000000..4832bd4 --- /dev/null +++ b/test cases/failing/71 skip only subdir/meson.build @@ -0,0 +1,8 @@ +# Check that skip_rest only exits subdir, not the whole script. +# Should create an error because main.cpp does not exists. +project('example exit', 'cpp') + +subdir('subdir') + +message('Good') +executable('main', 'main.cpp') diff --git a/test cases/failing/71 skip only subdir/subdir/meson.build b/test cases/failing/71 skip only subdir/subdir/meson.build new file mode 100644 index 0000000..1ba447b --- /dev/null +++ b/test cases/failing/71 skip only subdir/subdir/meson.build @@ -0,0 +1,3 @@ +subdir_done() + +error('Unreachable') diff --git a/test cases/unit/26 shared_mod linking/libfile.c b/test cases/unit/26 shared_mod linking/libfile.c new file mode 100644 index 0000000..44f7667 --- /dev/null +++ b/test cases/unit/26 shared_mod linking/libfile.c @@ -0,0 +1,14 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +int DLL_PUBLIC func() { + return 0; +} diff --git a/test cases/unit/26 shared_mod linking/main.c b/test cases/unit/26 shared_mod linking/main.c new file mode 100644 index 0000000..12f9c98 --- /dev/null +++ b/test cases/unit/26 shared_mod linking/main.c @@ -0,0 +1,11 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_IMPORT __declspec(dllimport) +#else + #define DLL_IMPORT +#endif + +int DLL_IMPORT func(); + +int main(int argc, char **arg) { + return func(); +} diff --git a/test cases/unit/26 shared_mod linking/meson.build b/test cases/unit/26 shared_mod linking/meson.build new file mode 100644 index 0000000..994a5d3 --- /dev/null +++ b/test cases/unit/26 shared_mod linking/meson.build @@ -0,0 +1,5 @@ +project('shared library linking test', 'c', 'cpp') + +mod = shared_module('mymod', 'libfile.c') + +exe = executable('prog', 'main.c', link_with : mod, install : true)
\ No newline at end of file |