aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/backend/backends.py9
-rw-r--r--mesonbuild/backend/ninjabackend.py39
-rw-r--r--mesonbuild/mesonmain.py3
-rw-r--r--mesonbuild/scripts/cleantrees.py43
4 files changed, 89 insertions, 5 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 8b6d181..bc61c96 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.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
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a20a35f..4c10e8d 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -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)
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 3c644a8..e85ef17 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -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
diff --git a/mesonbuild/scripts/cleantrees.py b/mesonbuild/scripts/cleantrees.py
new file mode 100644
index 0000000..0af8dd0
--- /dev/null
+++ b/mesonbuild/scripts/cleantrees.py
@@ -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:])