aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2017-07-04 10:01:12 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2017-08-01 11:40:28 +0100
commitd436869a89eee7c6e43ddf799386b16a4acdf74c (patch)
treeb45f818df84bbdd5f91f096b55b9d791bdda80b8
parent36e58e88650f0ef51cdee37ec7a593893298cadf (diff)
downloadmeson-d436869a89eee7c6e43ddf799386b16a4acdf74c.zip
meson-d436869a89eee7c6e43ddf799386b16a4acdf74c.tar.gz
meson-d436869a89eee7c6e43ddf799386b16a4acdf74c.tar.bz2
Remove directories created by ninja install
Introduce a DirMaker class that disassembles the path up to '/' and stores all directories in a list. That list is in creation order and pre-existing directories are ignored, i.e. creating the two paths '/usr/share/foo/bar/baz' and '/usr/share/foo/bar/boo' is stored as [ '/usr/share/foo', '/usr/share/foo/bar', '/usr/share/foo/bar/baz', '/usr/share/foo/bar/boo' ] This is on the assumption that /usr/share already existed. After all files have been installed, the list of created directories is appended in reverse order to the install log. The uninstall script then triggers rmdir on all directories. If a custom install script drops files into the directories, removing those will fail. This matches the current expectation, see the existing warning "Remember that files created by custom scripts have not been removed." Unfortunately, this makes the behavior on uninstall inconsistent. Assuming a non-existing prefix, ninja install && ninja uninstall removes all traces of the install. However, ninja install && ninja install && ninja uninstall removes the files only, not the directories as they already existed. This could be fixed by just unconditionally removing any (emtpy) directories that we drop files into, at the risk of removing system directories if empty. For example, one could imagine /etc/foo.conf.d/ to be removed in that case if it is empty. https://github.com/mesonbuild/meson/issues/2032
-rw-r--r--mesonbuild/scripts/meson_install.py62
-rw-r--r--mesonbuild/scripts/uninstall.py5
2 files changed, 50 insertions, 17 deletions
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index 35904ea..f61cbab 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -21,6 +21,35 @@ from ..mesonlib import is_windows, Popen_safe
install_log_file = None
use_selinux = True
+class DirMaker:
+ def __init__(self):
+ self.dirs = []
+
+ def makedirs(self, path, exist_ok=False):
+ dirname = os.path.normpath(path)
+ dirs = []
+ while dirname != os.path.dirname(dirname):
+ if not os.path.exists(dirname):
+ dirs.append(dirname)
+ dirname = os.path.dirname(dirname)
+ os.makedirs(path, exist_ok=exist_ok)
+
+ # store the directories in creation order, with the parent directory
+ # before the child directories. Future calls of makedir() will not
+ # create the parent directories, so the last element in the list is
+ # the last one to be created. That is the first one to be removed on
+ # __exit__
+ dirs.reverse()
+ self.dirs += dirs
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.dirs.reverse()
+ for d in self.dirs:
+ append_to_log(d)
+
def set_mode(path, mode):
if mode is None:
# Keep mode unchanged
@@ -99,7 +128,7 @@ def do_copyfile(from_file, to_file):
restore_selinux_context(to_file)
append_to_log(to_file)
-def do_copydir(src_prefix, src_dir, dst_dir):
+def do_copydir(data, src_prefix, src_dir, dst_dir):
'''
Copies the directory @src_prefix (full path) into @dst_dir
@@ -115,7 +144,7 @@ def do_copydir(src_prefix, src_dir, dst_dir):
if os.path.exists(abs_dst):
print('Tried to copy directory %s but a file of that name already exists.' % abs_dst)
sys.exit(1)
- os.makedirs(abs_dst)
+ data.dirmaker.makedirs(abs_dst)
shutil.copystat(abs_src, abs_dst)
for f in files:
abs_src = os.path.join(src_dir, root, f)
@@ -145,12 +174,14 @@ def do_install(datafilename):
d.destdir = os.environ.get('DESTDIR', '')
d.fullprefix = destdir_join(d.destdir, d.prefix)
- install_subdirs(d) # Must be first, because it needs to delete the old subtree.
- install_targets(d)
- install_headers(d)
- install_man(d)
- install_data(d)
- run_install_script(d)
+ d.dirmaker = DirMaker()
+ with d.dirmaker:
+ install_subdirs(d) # Must be first, because it needs to delete the old subtree.
+ install_targets(d)
+ install_headers(d)
+ install_man(d)
+ install_data(d)
+ run_install_script(d)
def install_subdirs(d):
for (src_dir, inst_dir, dst_dir, mode) in d.install_subdirs:
@@ -159,9 +190,8 @@ def install_subdirs(d):
src_prefix = os.path.join(src_dir, inst_dir)
print('Installing subdir %s to %s' % (src_prefix, dst_dir))
dst_dir = get_destdir_path(d, dst_dir)
- if not os.path.exists(dst_dir):
- os.makedirs(dst_dir)
- do_copydir(src_prefix, src_dir, dst_dir)
+ d.dirmaker.makedirs(dst_dir, exist_ok=True)
+ do_copydir(d, src_prefix, src_dir, dst_dir)
dst_prefix = os.path.join(dst_dir, inst_dir)
set_mode(dst_prefix, mode)
@@ -171,7 +201,7 @@ def install_data(d):
outfilename = get_destdir_path(d, i[1])
mode = i[2]
outdir = os.path.split(outfilename)[0]
- os.makedirs(outdir, exist_ok=True)
+ d.dirmaker.makedirs(outdir, exist_ok=True)
print('Installing %s to %s' % (fullfilename, outdir))
do_copyfile(fullfilename, outfilename)
set_mode(outfilename, mode)
@@ -181,7 +211,7 @@ def install_man(d):
full_source_filename = m[0]
outfilename = get_destdir_path(d, m[1])
outdir = os.path.split(outfilename)[0]
- os.makedirs(outdir, exist_ok=True)
+ d.dirmaker.makedirs(outdir, exist_ok=True)
print('Installing %s to %s' % (full_source_filename, outdir))
if outfilename.endswith('.gz') and not full_source_filename.endswith('.gz'):
with open(outfilename, 'wb') as of:
@@ -199,7 +229,7 @@ def install_headers(d):
outdir = get_destdir_path(d, t[1])
outfilename = os.path.join(outdir, fname)
print('Installing %s to %s' % (fname, outdir))
- os.makedirs(outdir, exist_ok=True)
+ d.dirmaker.makedirs(outdir, exist_ok=True)
do_copyfile(fullfilename, outfilename)
def run_install_script(d):
@@ -264,7 +294,7 @@ def install_targets(d):
should_strip = t[3]
install_rpath = t[4]
print('Installing %s to %s' % (fname, outname))
- os.makedirs(outdir, exist_ok=True)
+ d.dirmaker.makedirs(outdir, exist_ok=True)
if not os.path.exists(fname):
raise RuntimeError('File {!r} could not be found'.format(fname))
elif os.path.isfile(fname):
@@ -287,7 +317,7 @@ def install_targets(d):
do_copyfile(pdb_filename, pdb_outname)
elif os.path.isdir(fname):
fname = os.path.join(d.build_dir, fname.rstrip('/'))
- do_copydir(fname, os.path.dirname(fname), outdir)
+ do_copydir(d, fname, os.path.dirname(fname), outdir)
else:
raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False
diff --git a/mesonbuild/scripts/uninstall.py b/mesonbuild/scripts/uninstall.py
index 1480921..bdc036b 100644
--- a/mesonbuild/scripts/uninstall.py
+++ b/mesonbuild/scripts/uninstall.py
@@ -24,7 +24,10 @@ def do_uninstall(log):
continue
fname = line.strip()
try:
- os.unlink(fname)
+ if os.path.isdir(fname) and not os.path.islink(fname):
+ os.rmdir(fname)
+ else:
+ os.unlink(fname)
print('Deleted:', fname)
successes += 1
except Exception as e: