aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-01-28 19:05:54 +0200
committerGitHub <noreply@github.com>2017-01-28 19:05:54 +0200
commit4a08841331cf0bb281ff5fa37ac106b539423140 (patch)
treeb9f14dbee8c924d8910af2b8607dee438f6ee779
parent6357ad0cd051d56deeb6c029a436b47b1f5216ae (diff)
parent534ee8baa8b807b2adf52937a4672897f1a6d8cb (diff)
downloadmeson-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…
-rw-r--r--mesonbuild/backend/backends.py23
-rw-r--r--mesonbuild/backend/ninjabackend.py9
-rw-r--r--mesonbuild/backend/vs2010backend.py88
-rw-r--r--mesonbuild/dependencies.py12
-rwxr-xr-xmesontest.py4
-rwxr-xr-xrun_project_tests.py48
-rwxr-xr-xrun_unittests.py2
-rw-r--r--test cases/common/117 custom target capture/meson.build15
-rw-r--r--test cases/common/136 build by default targets in tests/main.c3
-rw-r--r--test cases/common/136 build by default targets in tests/meson.build23
-rw-r--r--test cases/common/136 build by default targets in tests/write_file.py6
-rw-r--r--test cases/common/137 build by default/foo.c (renamed from test cases/unit/5 build by default/foo.c)0
-rw-r--r--test cases/common/137 build by default/meson.build24
-rw-r--r--test cases/common/137 build by default/mygen.py (renamed from test cases/unit/5 build by default/mygen.py)0
-rw-r--r--test cases/common/137 build by default/source.txt (renamed from test cases/unit/5 build by default/source.txt)0
-rw-r--r--test cases/unit/5 build by default/meson.build13
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,
-)