Use a script to clean-up custom-target output dirs

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.

Closes #1220
pull/1227/head
Nirbheek Chauhan 8 years ago
parent c854ae1801
commit 8e06ab6a95
  1. 9
      mesonbuild/backend/backends.py
  2. 39
      mesonbuild/backend/ninjabackend.py
  3. 3
      mesonbuild/mesonmain.py
  4. 43
      mesonbuild/scripts/cleantrees.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,14 +2152,19 @@ 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):
continue
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
targetlist.append(os.path.join(self.get_target_dir(t), t.get_outputs()[0]))
@ -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)

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