Merge pull request #1227 from centricular/ninja-clean-recursive-dir-delete

run_project_tests.py: Also do ninja clean on tests
pull/1239/head
Jussi Pakkanen 8 years ago committed by GitHub
commit 4317edca25
  1. 9
      mesonbuild/backend/backends.py
  2. 37
      mesonbuild/backend/ninjabackend.py
  3. 1
      mesonbuild/coredata.py
  4. 3
      mesonbuild/mesonmain.py
  5. 43
      mesonbuild/scripts/cleantrees.py
  6. 55
      run_project_tests.py

@ -22,6 +22,15 @@ import json
import subprocess
from ..mesonlib import MesonException, get_compiler_for_source, classify_unity_sources
class CleanTrees():
'''
Directories outputted by custom targets that have to be manually cleaned
because on Linux `ninja clean` only deletes empty directories.
'''
def __init__(self, build_dir, trees):
self.build_dir = build_dir
self.trees = trees
class InstallData():
def __init__(self, source_dir, build_dir, prefix):
self.source_dir = source_dir

@ -20,7 +20,7 @@ from .. import mlog
from .. import dependencies
from .. import compilers
from ..mesonlib import File, MesonException, get_compiler_for_source, Popen_safe
from .backends import InstallData
from .backends import CleanTrees, InstallData
from ..build import InvalidArguments
import os, sys, pickle, re
import subprocess, shutil
@ -2109,6 +2109,22 @@ rule FORTRAN_DEP_HACK
except OSError:
mlog.debug("Library versioning disabled because we do not have symlink creation privileges.")
def generate_custom_target_clean(self, outfile, trees):
e = NinjaBuildElement(self.all_outputs, 'clean-ctlist', 'CUSTOM_COMMAND', 'PHONY')
d = CleanTrees(self.environment.get_build_dir(), trees)
d_file = os.path.join(self.environment.get_scratch_dir(), 'cleantrees.dat')
script_root = self.environment.get_script_dir()
clean_script = os.path.join(script_root, 'cleantrees.py')
e.add_item('COMMAND', [sys.executable,
self.environment.get_build_command(),
'--internal', 'cleantrees', d_file])
e.add_item('description', 'Cleaning CustomTarget directories')
e.write(outfile)
# Write out the data file passed to the script
with open(d_file, 'wb') as ofile:
pickle.dump(d, ofile)
return 'clean-ctlist'
def generate_gcov_clean(self, outfile):
gcno_elem = NinjaBuildElement(self.all_outputs, 'clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
script_root = self.environment.get_script_dir()
@ -2136,13 +2152,18 @@ rule FORTRAN_DEP_HACK
def generate_ending(self, outfile):
targetlist = []
ctlist = []
for t in self.build.get_targets().values():
# RunTargets are meant to be invoked manually
if isinstance(t, build.RunTarget):
continue
# CustomTargets that aren't installed should only be built if they
# are used by something else or are meant to be always built
if isinstance(t, build.CustomTarget) and not (t.install or t.build_always):
if isinstance(t, build.CustomTarget):
# Create a list of all custom target outputs
for o in t.get_outputs():
ctlist.append(os.path.join(self.get_target_dir(t), o))
# CustomTargets that aren't installed should only be built if
# they are used by something else or are to always be built
if not (t.install or t.build_always):
continue
# Add the first output of each target to the 'all' target so that
# they are all built
@ -2160,6 +2181,14 @@ rule FORTRAN_DEP_HACK
elem = NinjaBuildElement(self.all_outputs, 'clean', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', [ninja_command, '-t', 'clean'])
elem.add_item('description', 'Cleaning')
# If we have custom targets in this project, add all their outputs to
# the list that is passed to the `cleantrees.py` script. The script
# will manually delete all custom_target outputs that are directories
# instead of files. This is needed because on platforms other than
# Windows, Ninja only deletes directories while cleaning if they are
# empty. https://github.com/mesonbuild/meson/issues/1220
if ctlist:
elem.add_dep(self.generate_custom_target_clean(outfile, ctlist))
if 'b_coverage' in self.environment.coredata.base_options and \
self.environment.coredata.base_options['b_coverage'].value:
self.generate_gcov_clean(outfile)

@ -236,6 +236,7 @@ builtin_options = {
}
forbidden_target_names = {'clean': None,
'clean-ctlist': None,
'clean-gcno': None,
'clean-gcda': None,
'coverage-text': None,

@ -198,6 +198,9 @@ def run_script_command(args):
if cmdname == 'exe':
import mesonbuild.scripts.meson_exe as abc
cmdfunc = abc.run
elif cmdname == 'cleantrees':
import mesonbuild.scripts.cleantrees as abc
cmdfunc = abc.run
elif cmdname == 'install':
import mesonbuild.scripts.meson_install as abc
cmdfunc = abc.run

@ -0,0 +1,43 @@
# Copyright 2016 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import shutil
import pickle
def rmtrees(build_dir, trees):
for t in trees:
# Never delete trees outside of the builddir
if os.path.isabs(t):
print('Cannot delete dir with absolute path {!r}'.format(t))
continue
bt = os.path.join(build_dir, t)
# Skip if it doesn't exist, or if it is not a directory
if os.path.isdir(bt):
shutil.rmtree(bt, ignore_errors=True)
def run(args):
if len(args) != 1:
print('Cleaner script for Meson. Do not run on your own please.')
print('cleantrees.py <data-file>')
return 1
with open(args[0], 'rb') as f:
data = pickle.load(f)
rmtrees(data.build_dir, data.trees)
# Never fail cleaning
return 0
if __name__ == '__main__':
run(sys.argv[1:])

@ -24,7 +24,7 @@ from mesonbuild import environment
from mesonbuild import mesonlib
from mesonbuild import mlog
from mesonbuild import mesonmain
from mesonbuild.mesonlib import stringlistify
from mesonbuild.mesonlib import stringlistify, Popen_safe
import argparse
import xml.etree.ElementTree as ET
import time
@ -93,26 +93,20 @@ unity_flags = []
backend_flags = None
compile_commands = None
test_commands = None
install_commands = None
install_commands = []
clean_commands = []
def setup_commands(backend):
global backend_flags, compile_commands, test_commands, install_commands
global backend_flags, compile_commands, test_commands, install_commands, clean_commands
msbuild_exe = shutil.which('msbuild')
if backend == 'vs2010' or (backend is None and msbuild_exe is not None):
backend_flags = ['--backend=vs2010']
if (backend and backend.startswith('vs')) or (backend is None and msbuild_exe is not None):
backend_flags = ['--backend=' + backend]
compile_commands = ['msbuild']
test_commands = ['msbuild', 'RUN_TESTS.vcxproj']
install_commands = []
elif backend == 'vs2015':
backend_flags = ['--backend=vs2015']
compile_commands = ['msbuild']
test_commands = ['msbuild', 'RUN_TESTS.vcxproj']
install_commands = []
elif backend == 'xcode' or (backend is None and mesonlib.is_osx()):
backend_flags = ['--backend=xcode']
compile_commands = ['xcodebuild']
test_commands = ['xcodebuild', '-target', 'RUN_TESTS']
install_commands = []
else:
backend_flags = []
ninja_command = environment.detect_ninja()
@ -125,6 +119,7 @@ def setup_commands(backend):
compile_commands += ['-w', 'dupbuild=err']
test_commands = [ninja_command, 'test', 'benchmark']
install_commands = [ninja_command, 'install']
clean_commands = [ninja_command, 'clean']
def get_relative_files_list_from_dir(fromdir):
paths = []
@ -233,17 +228,18 @@ def parse_test_args(testdir):
pass
return args
def run_test(skipped, testdir, extra_args, flags, compile_commands, install_commands, should_fail):
def run_test(skipped, testdir, extra_args, flags, compile_commands, should_fail):
if skipped:
return None
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
with AutoDeletedDir(tempfile.mkdtemp(prefix='i ', dir=os.getcwd())) as install_dir:
try:
return _run_test(testdir, build_dir, install_dir, extra_args, flags, compile_commands, install_commands, should_fail)
return _run_test(testdir, build_dir, install_dir, extra_args, flags, compile_commands, should_fail)
finally:
mlog.shutdown() # Close the log file because otherwise Windows wets itself.
def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_commands, install_commands, should_fail):
def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_commands, should_fail):
global install_commands, clean_commands
test_args = parse_test_args(testdir)
gen_start = time.time()
gen_command = [meson_command, '--prefix', '/usr', '--libdir', 'lib', testdir, test_build_dir]\
@ -268,12 +264,10 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c
else:
comp = compile_commands
build_start = time.time()
pc = subprocess.Popen(comp, cwd=test_build_dir,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(o, e) = pc.communicate()
pc, o, e = Popen_safe(comp, cwd=test_build_dir)
build_time = time.time() - build_start
stdo += o.decode(sys.stdout.encoding)
stde += e.decode(sys.stdout.encoding)
stdo += o
stde += e
if should_fail == 'build':
if pc.returncode != 0:
return TestResult('', stdo, stde, mesonlog, gen_time)
@ -294,18 +288,23 @@ 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)
else:
env = os.environ.copy()
env['DESTDIR'] = install_dir
pi = subprocess.Popen(install_commands, cwd=test_build_dir, env=env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(o, e) = pi.communicate()
stdo += o.decode(sys.stdout.encoding)
stde += e.decode(sys.stdout.encoding)
pi, o, e = Popen_safe(install_commands, cwd=test_build_dir, env=env)
stdo += o
stde += e
if pi.returncode != 0:
return TestResult('Running install failed.', stdo, stde, mesonlog, gen_time, build_time, test_time)
if len(clean_commands) != 0:
env = os.environ.copy()
pi, o, e = Popen_safe(clean_commands, cwd=test_build_dir, env=env)
stdo += o
stde += e
if pi.returncode != 0:
return TestResult('Running clean failed.', stdo, stde, mesonlog, gen_time, build_time, test_time)
return TestResult(validate_install(testdir, install_dir), stdo, stde, mesonlog, gen_time, build_time, test_time)
def gather_tests(testdir):
@ -372,7 +371,7 @@ def detect_tests_to_run():
return all_tests
def run_tests(extra_args):
global passing_tests, failing_tests, stop, executor, futures
global install_commands, passing_tests, failing_tests, stop, executor, futures
all_tests = detect_tests_to_run()
logfile = open('meson-test-run.txt', 'w', encoding="utf_8")
junit_root = ET.Element('testsuites')
@ -404,7 +403,7 @@ def run_tests(extra_args):
should_fail = False
if name.startswith('failing'):
should_fail = name.split('failing-')[1]
result = executor.submit(run_test, skipped, t, extra_args, unity_flags + backend_flags, compile_commands, install_commands, should_fail)
result = executor.submit(run_test, skipped, t, extra_args, unity_flags + backend_flags, compile_commands, should_fail)
futures.append((testname, t, result))
for (testname, t, result) in futures:
result = result.result()

Loading…
Cancel
Save