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…
pull/1347/head
Jussi Pakkanen 8 years ago committed by GitHub
commit 4a08841331
  1. 23
      mesonbuild/backend/backends.py
  2. 9
      mesonbuild/backend/ninjabackend.py
  3. 88
      mesonbuild/backend/vs2010backend.py
  4. 12
      mesonbuild/dependencies.py
  5. 4
      mesontest.py
  6. 48
      run_project_tests.py
  7. 2
      run_unittests.py
  8. 15
      test cases/common/117 custom target capture/meson.build
  9. 3
      test cases/common/136 build by default targets in tests/main.c
  10. 23
      test cases/common/136 build by default targets in tests/meson.build
  11. 6
      test cases/common/136 build by default targets in tests/write_file.py
  12. 0
      test cases/common/137 build by default/foo.c
  13. 24
      test cases/common/137 build by default/meson.build
  14. 0
      test cases/common/137 build by default/mygen.py
  15. 0
      test cases/common/137 build by default/source.txt
  16. 13
      test cases/unit/5 build by default/meson.build

@ -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():

@ -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():

@ -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)

@ -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

@ -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')):

@ -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()

@ -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')

@ -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])

@ -0,0 +1,3 @@
int main (int argc, char *argv[]) {
return 0;
}

@ -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])

@ -0,0 +1,6 @@
#!/usr/bin/env python3
import sys
with open(sys.argv[1], 'w') as f:
f.write('Test')

@ -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])

@ -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,
)
Loading…
Cancel
Save