diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2017-01-28 19:05:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-28 19:05:54 +0200 |
commit | 4a08841331cf0bb281ff5fa37ac106b539423140 (patch) | |
tree | b9f14dbee8c924d8910af2b8607dee438f6ee779 | |
parent | 6357ad0cd051d56deeb6c029a436b47b1f5216ae (diff) | |
parent | 534ee8baa8b807b2adf52937a4672897f1a6d8cb (diff) | |
download | meson-4a08841331cf0bb281ff5fa37ac106b539423140.zip meson-4a08841331cf0bb281ff5fa37ac106b539423140.tar.gz meson-4a08841331cf0bb281ff5fa37ac106b539423140.tar.bz2 |
Merge pull request #1335 from tp-m/test-custom-target-used-in-test-cmd
tests: check custom target output is created before being used in a t…
16 files changed, 199 insertions, 71 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4988f28..46f8563 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -544,6 +544,29 @@ class Backend: newargs.append(arg) return newargs + def get_build_by_default_targets(self): + result = {} + # Get all build and custom targets that must be built by default + for name, t in self.build.get_targets().items(): + if t.build_by_default or t.install or t.build_always: + result[name] = t + # Get all targets used as test executables and arguments. These must + # also be built by default. XXX: Sometime in the future these should be + # built only before running tests. + for t in self.build.get_tests(): + exe = t.exe + if hasattr(exe, 'held_object'): + exe = exe.held_object + if isinstance(exe, (build.CustomTarget, build.BuildTarget)): + result[exe.get_id()] = exe + for arg in t.cmd_args: + if hasattr(arg, 'held_object'): + arg = arg.held_object + if not isinstance(arg, (build.CustomTarget, build.BuildTarget)): + continue + result[arg.get_id()] = arg + return result + def get_custom_target_provided_libraries(self, target): libs = [] for t in target.get_generated_sources(): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9444087..c8ac35c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2254,17 +2254,10 @@ rule FORTRAN_DEP_HACK elem.add_item('pool', 'console') elem.write(outfile) - def get_build_by_default_targets(self): - result = [] - for t in self.build.get_targets().values(): - if t.build_by_default or t.install or t.build_always: - result.append(t) - return result - def generate_ending(self, outfile): targetlist = [] ctlist = [] - for t in self.get_build_by_default_targets(): + for t in self.get_build_by_default_targets().values(): if isinstance(t, build.CustomTarget): # Create a list of all custom target outputs for o in t.get_outputs(): diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 3b79a9c..666da7d 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -192,35 +192,40 @@ class Vs2010Backend(backends.Backend): result = {} for o in obj_list: if isinstance(o, build.ExtractedObjects): - result[o.target.get_id()] = True - return result.keys() + result[o.target.get_id()] = o.target + return result.items() - def determine_deps(self, p): + def get_target_deps(self, t, recursive=False): all_deps = {} - target = self.build.targets[p[0]] - if isinstance(target, build.CustomTarget): - for d in target.get_target_dependencies(): - all_deps[d.get_id()] = True - return all_deps - if isinstance(target, build.RunTarget): - for d in [target.command] + target.args: - if isinstance(d, build.BuildTarget): - all_deps[d.get_id()] = True - return all_deps - for ldep in target.link_targets: - all_deps[ldep.get_id()] = True - for objdep in self.get_obj_target_deps(target.objects): - all_deps[objdep] = True - for gendep in target.generated: - if isinstance(gendep, build.CustomTarget): - all_deps[gendep.get_id()] = True + for target in t.values(): + if isinstance(target, build.CustomTarget): + for d in target.get_target_dependencies(): + all_deps[d.get_id()] = d + elif isinstance(target, build.RunTarget): + for d in [target.command] + target.args: + if isinstance(d, (build.BuildTarget, build.CustomTarget)): + all_deps[d.get_id()] = d + # BuildTarget else: - gen_exe = gendep.generator.get_exe() - if isinstance(gen_exe, build.Executable): - all_deps[gen_exe.get_id()] = True - return all_deps + for ldep in target.link_targets: + all_deps[ldep.get_id()] = ldep + for obj_id, objdep in self.get_obj_target_deps(target.objects): + all_deps[obj_id] = objdep + for gendep in target.get_generated_sources(): + if isinstance(gendep, build.CustomTarget): + all_deps[gendep.get_id()] = gendep + else: + gen_exe = gendep.generator.get_exe() + if isinstance(gen_exe, build.Executable): + all_deps[gen_exe.get_id()] = gen_exe + if not t or not recursive: + return all_deps + ret = self.get_target_deps(all_deps, recursive) + ret.update(all_deps) + return ret def generate_solution(self, sln_filename, projlist): + default_projlist = self.get_build_by_default_targets() with open(sln_filename, 'w') as ofile: ofile.write('Microsoft Visual Studio Solution File, Format ' 'Version 11.00\n') @@ -230,7 +235,12 @@ class Vs2010Backend(backends.Backend): prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) ofile.write(prj_line) - all_deps = self.determine_deps(p) + target = self.build.targets[p[0]] + t = {target.get_id(): target} + # Get direct deps + all_deps = self.get_target_deps(t) + # Get recursive deps + recursive_deps = self.get_target_deps(t, recursive=True) ofile.write('\tProjectSection(ProjectDependencies) = ' 'postProject\n') regen_guid = self.environment.coredata.regen_guid @@ -240,6 +250,9 @@ class Vs2010Backend(backends.Backend): ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) ofile.write('EndProjectSection\n') ofile.write('EndProject\n') + for dep, target in recursive_deps.items(): + if p[0] in default_projlist: + default_projlist[dep] = target test_line = prj_templ % (self.environment.coredata.guid, 'RUN_TESTS', 'RUN_TESTS.vcxproj', self.environment.coredata.test_guid) @@ -265,11 +278,15 @@ class Vs2010Backend(backends.Backend): ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % (self.environment.coredata.regen_guid, self.buildtype, self.platform, self.buildtype, self.platform)) + # Create the solution configuration for p in projlist: + # Add to the list of projects in this solution ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % (p[2], self.buildtype, self.platform, self.buildtype, self.platform)) - if not isinstance(self.build.targets[p[0]], build.RunTarget): + if p[0] in default_projlist and \ + not isinstance(self.build.targets[p[0]], build.RunTarget): + # Add to the list of projects to be built ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % (p[2], self.buildtype, self.platform, self.buildtype, self.platform)) @@ -1106,12 +1123,19 @@ if %%errorlevel%% neq 0 goto :VCEnd''' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' postbuild = ET.SubElement(action, 'PostBuildEvent') ET.SubElement(postbuild, 'Message') + # FIXME: No benchmarks? + meson_py = self.environment.get_build_command() + (base, ext) = os.path.splitext(meson_py) + mesontest_py = base + 'test' + ext test_command = [sys.executable, - self.environment.get_build_command(), - '--internal', - 'test'] + mesontest_py, + '--no-rebuild'] + if not self.environment.coredata.get_builtin_option('stdsplit'): + test_command += ['--no-stdsplit'] + if self.environment.coredata.get_builtin_option('errorlogs'): + test_command += ['--print-errorlogs'] cmd_templ = '''setlocal -"%s" "%s" +"%s" if %%errorlevel%% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone @@ -1119,9 +1143,9 @@ endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone exit /b %%1 :cmDone if %%errorlevel%% neq 0 goto :VCEnd''' - test_data = self.serialise_tests()[0] + self.serialise_tests() ET.SubElement(postbuild, 'Command').text =\ - cmd_templ % ('" "'.join(test_command), test_data) + cmd_templ % ('" "'.join(test_command)) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') tree = ET.ElementTree(root) tree.write(ofname, encoding='utf-8', xml_declaration=True) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index b01e0a8..5ccc391 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -20,6 +20,7 @@ # package before this gets too big. import re +import sys import os, stat, glob, shutil import subprocess import sysconfig @@ -426,10 +427,15 @@ class ExternalProgram: if first_line.startswith('#!'): commands = first_line[2:].split('#')[0].strip().split() if mesonlib.is_windows(): - # Windows does not have /usr/bin. - commands[0] = commands[0].split('/')[-1] - if commands[0] == 'env': + # Windows does not have UNIX paths so remove them, + # but don't remove Windows paths + if commands[0].startswith('/'): + commands[0] = commands[0].split('/')[-1] + if len(commands) > 0 and commands[0] == 'env': commands = commands[1:] + # Windows does not ship python3.exe, but we know the path to it + if len(commands) > 0 and commands[0] == 'python3': + commands[0] = sys.executable return commands + [script] except Exception: pass diff --git a/mesontest.py b/mesontest.py index 7d443c7..f5da103 100755 --- a/mesontest.py +++ b/mesontest.py @@ -162,9 +162,9 @@ class TestHarness: self.tests = None self.suites = None if self.options.benchmark: - self.load_datafile(os.path.join(options.wd, 'meson-private/meson_benchmark_setup.dat')) + self.load_datafile(os.path.join(options.wd, 'meson-private', 'meson_benchmark_setup.dat')) else: - self.load_datafile(os.path.join(options.wd, 'meson-private/meson_test_setup.dat')) + self.load_datafile(os.path.join(options.wd, 'meson-private', 'meson_test_setup.dat')) def rebuild_all(self): if not os.path.isfile(os.path.join(self.options.wd, 'build.ninja')): diff --git a/run_project_tests.py b/run_project_tests.py index e04fed1..f87a121 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -129,6 +129,14 @@ def setup_commands(backend): install_commands = [ninja_command, 'install'] clean_commands = [ninja_command, 'clean'] +def get_compile_commands_for_dir(compile_commands, test_build_dir): + if 'msbuild' in compile_commands[0]: + sln_name = glob(os.path.join(test_build_dir, '*.sln'))[0] + comp = compile_commands + [os.path.split(sln_name)[-1]] + else: + comp = compile_commands + return comp + def get_relative_files_list_from_dir(fromdir): paths = [] for (root, _, files) in os.walk(fromdir): @@ -250,6 +258,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c global install_commands, clean_commands test_args = parse_test_args(testdir) gen_start = time.time() + # Configure in-process gen_command = [meson_command, '--prefix', '/usr', '--libdir', 'lib', testdir, test_build_dir]\ + flags + test_args + extra_args (returncode, stdo, stde) = run_configure_inprocess(gen_command) @@ -266,11 +275,8 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c return TestResult('Test that should have failed succeeded', stdo, stde, mesonlog, gen_time) if returncode != 0: return TestResult('Generating the build system failed.', stdo, stde, mesonlog, gen_time) - if 'msbuild' in compile_commands[0]: - sln_name = glob(os.path.join(test_build_dir, '*.sln'))[0] - comp = compile_commands + [os.path.split(sln_name)[-1]] - else: - comp = compile_commands + # Build with subprocess + comp = get_compile_commands_for_dir(compile_commands, test_build_dir) build_start = time.time() pc, o, e = Popen_safe(comp, cwd=test_build_dir) build_time = time.time() - build_start @@ -288,9 +294,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c time.sleep(0.2) os.utime(os.path.join(testdir, 'meson.build')) test_start = time.time() - # Note that we don't test that running e.g. 'ninja test' actually - # works. One hopes that this is a common enough happening that - # it is picked up immediately on development. + # Test in-process (returncode, tstdo, tstde) = run_test_inprocess(test_build_dir) test_time = time.time() - test_start stdo += tstdo @@ -301,11 +305,11 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c return TestResult('Test that should have failed to run unit tests succeeded', stdo, stde, mesonlog, gen_time) if returncode != 0: return TestResult('Running unit tests failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) - # Do installation if len(install_commands) == 0: return TestResult('', '', '', gen_time, build_time, test_time) env = os.environ.copy() env['DESTDIR'] = install_dir + # Install with subprocess pi, o, e = Popen_safe(install_commands, cwd=test_build_dir, env=env) stdo += o stde += e @@ -313,6 +317,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c return TestResult('Running install failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) if len(clean_commands) != 0: env = os.environ.copy() + # Clean with subprocess pi, o, e = Popen_safe(clean_commands, cwd=test_build_dir, env=env) stdo += o stde += e @@ -526,6 +531,30 @@ def generate_prebuilt(): stlibfile = generate_pb_static(compiler, object_suffix, static_suffix) return objectfile, stlibfile +def check_meson_commands_work(): + global meson_command, compile_commands, test_commands, install_commands + testdir = 'test cases/common/1 trivial' + with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir: + print('Checking that configuring works...') + gen_cmd = [sys.executable, meson_command, testdir, build_dir] + backend_flags + pc, o, e = Popen_safe(gen_cmd) + if pc.returncode != 0: + raise RuntimeError('Failed to configure {!r}:\n{}\n{}'.format(testdir, e, o)) + print('Checking that building works...') + compile_cmd = get_compile_commands_for_dir(compile_commands, build_dir) + pc, o, e = Popen_safe(compile_cmd, cwd=build_dir) + if pc.returncode != 0: + raise RuntimeError('Failed to build {!r}:\n{}\n{}'.format(testdir, e, o)) + print('Checking that testing works...') + pc, o, e = Popen_safe(test_commands, cwd=build_dir) + if pc.returncode != 0: + raise RuntimeError('Failed to test {!r}:\n{}\n{}'.format(testdir, e, o)) + if install_commands: + print('Checking that installing works...') + pc, o, e = Popen_safe(install_commands, cwd=build_dir) + if pc.returncode != 0: + raise RuntimeError('Failed to install {!r}:\n{}\n{}'.format(testdir, e, o)) + if __name__ == '__main__': parser = argparse.ArgumentParser(description="Run the test suite of Meson.") parser.add_argument('extra_args', nargs='*', @@ -554,6 +583,7 @@ if __name__ == '__main__': if script_dir != '': os.chdir(script_dir) check_format() + check_meson_commands_work() pbfiles = generate_prebuilt() try: all_tests = detect_tests_to_run() diff --git a/run_unittests.py b/run_unittests.py index b6ea073..81bcd47 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -624,7 +624,7 @@ class LinuxlikeTests(unittest.TestCase): self._test_stds_impl(testdir, cpp, 'cpp') def test_build_by_default(self): - testdir = os.path.join(self.unit_test_dir, '5 build by default') + testdir = os.path.join(self.common_test_dir, '137 build by default') self.init(testdir) self.build() genfile = os.path.join(self.builddir, 'generated.dat') diff --git a/test cases/common/117 custom target capture/meson.build b/test cases/common/117 custom target capture/meson.build index d3214e8..16b6ac9 100644 --- a/test cases/common/117 custom target capture/meson.build +++ b/test cases/common/117 custom target capture/meson.build @@ -1,8 +1,9 @@ project('custom target', 'c') -python3 = find_program('python3', required : false) -if not python3.found() - python3 = find_program('python') +python3 = import('python3').find_python() + +if meson.backend().startswith('vs') + error('MESON_SKIP_TEST: capturing of custom target output is broken with the VS backends') endif # Note that this will not add a dependency to the compiler executable. @@ -17,3 +18,11 @@ mytarget = custom_target('bindat', install : true, install_dir : 'subdir' ) + +ct_output_exists = '''import os, sys +if not os.path.exists(sys.argv[1]): + print("could not find {!r} in {!r}".format(sys.argv[1], os.getcwd())) + sys.exit(1) +''' + +test('capture-wrote', python3, args : ['-c', ct_output_exists, mytarget]) diff --git a/test cases/common/136 build by default targets in tests/main.c b/test cases/common/136 build by default targets in tests/main.c new file mode 100644 index 0000000..63a62a1 --- /dev/null +++ b/test cases/common/136 build by default targets in tests/main.c @@ -0,0 +1,3 @@ +int main (int argc, char *argv[]) { + return 0; +} diff --git a/test cases/common/136 build by default targets in tests/meson.build b/test cases/common/136 build by default targets in tests/meson.build new file mode 100644 index 0000000..5cc5055 --- /dev/null +++ b/test cases/common/136 build by default targets in tests/meson.build @@ -0,0 +1,23 @@ +project('unit-test', 'c', version : '1.0') + +write_file = find_program('write_file.py') + +# A test that consumes and verifies the output generated by a custom target. +# Should work even if target is not built by default. Makes sure that foo.out +# is actually created before the test command that uses foo_out is run. +foo_out = custom_target('foo.out', + output : 'foo.out', + command : [write_file, '@OUTPUT@']) + +# Also verify that a build_by_default : false BuildTarget added to a test is +# built before the test is run. +exe_out = executable('out', 'main.c', build_by_default : false) + +py_file_exists = '''import os, sys +if not os.path.exists(sys.argv[1]) or not os.path.exists(sys.argv[2]): + print("could not find {!r} or {!r} in {!r}" + "".format(sys.argv[1], sys.argv[2], os.getcwd())) + sys.exit(1)''' + +python = import('python3').find_python() +test('output-check', python, args : ['-c', py_file_exists, foo_out, exe_out]) diff --git a/test cases/common/136 build by default targets in tests/write_file.py b/test cases/common/136 build by default targets in tests/write_file.py new file mode 100644 index 0000000..ff9c224 --- /dev/null +++ b/test cases/common/136 build by default targets in tests/write_file.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys + +with open(sys.argv[1], 'w') as f: + f.write('Test') diff --git a/test cases/unit/5 build by default/foo.c b/test cases/common/137 build by default/foo.c index ca97916..ca97916 100644 --- a/test cases/unit/5 build by default/foo.c +++ b/test cases/common/137 build by default/foo.c diff --git a/test cases/common/137 build by default/meson.build b/test cases/common/137 build by default/meson.build new file mode 100644 index 0000000..6569548 --- /dev/null +++ b/test cases/common/137 build by default/meson.build @@ -0,0 +1,24 @@ +project('build on all', 'c') + +py3_mod = import('python3') +py3 = py3_mod.find_python() + +executable('fooprog', 'foo.c', build_by_default : false) +comp = files('mygen.py') +mytarget = custom_target('gendat', + output : 'generated.dat', + input : 'source.txt', + command : [py3] + comp + ['@INPUT@', '@OUTPUT@'], + build_by_default : true, +) + +ct_output = join_paths(meson.build_root(), 'generated.dat') +exe_output = join_paths(meson.build_root(), 'fooprog') +if host_machine.system() == 'windows' + exe_output += '.exe' +endif + +ct_exists_exe_nexists = 'import os.path, sys; sys.exit(not os.path.exists(sys.argv[1]) and os.path.exists(sys.argv[2]))' + +test('check-build-by-default', py3, + args : ['-c', ct_exists_exe_nexists, ct_output, exe_output]) diff --git a/test cases/unit/5 build by default/mygen.py b/test cases/common/137 build by default/mygen.py index 5a74153..5a74153 100644 --- a/test cases/unit/5 build by default/mygen.py +++ b/test cases/common/137 build by default/mygen.py diff --git a/test cases/unit/5 build by default/source.txt b/test cases/common/137 build by default/source.txt index 3573f4b..3573f4b 100644 --- a/test cases/unit/5 build by default/source.txt +++ b/test cases/common/137 build by default/source.txt diff --git a/test cases/unit/5 build by default/meson.build b/test cases/unit/5 build by default/meson.build deleted file mode 100644 index 67c5cc2..0000000 --- a/test cases/unit/5 build by default/meson.build +++ /dev/null @@ -1,13 +0,0 @@ -project('build on all', 'c') - -py3_mod = import('python3') -py3 = py3_mod.find_python() - -executable('fooprog', 'foo.c', build_by_default : false) -comp = files('mygen.py') -mytarget = custom_target('gendat', - output : 'generated.dat', - input : 'source.txt', - command : [py3] + comp + ['@INPUT@', '@OUTPUT@'], - build_by_default : true, -) |