From 5cb1d00537afb9d52f89f7b3dc65e01f068fd442 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 20 Jun 2017 01:53:39 -0400 Subject: [PATCH] Allow excluding files from `install_subdir` The install_subdir command now accepts a new `exclude` keyword argument that allows specified files to be excluded from the installed subdirectory. --- docs/markdown/Reference-manual.md | 10 +++++- docs/markdown/Release-notes-for-0.42.0.md | 6 ++++ mesonbuild/backend/ninjabackend.py | 2 +- mesonbuild/interpreter.py | 33 +++++++++++++++++-- mesonbuild/scripts/meson_install.py | 20 ++++++++--- .../66 install subdir/installed_files.txt | 2 ++ .../common/66 install subdir/meson.build | 6 ++++ .../sub2/dircheck/excluded-three.dat | 0 .../66 install subdir/sub2/excluded-three.dat | 0 .../66 install subdir/sub2/excluded/two.dat | 0 .../common/66 install subdir/sub2/one.dat | 0 11 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 test cases/common/66 install subdir/sub2/dircheck/excluded-three.dat create mode 100644 test cases/common/66 install subdir/sub2/excluded-three.dat create mode 100644 test cases/common/66 install subdir/sub2/excluded/two.dat create mode 100644 test cases/common/66 install subdir/sub2/one.dat diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index ebd7b394e..0008e4fdb 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -474,11 +474,19 @@ Installs the specified man files from the source tree into system's man director ### install_subdir() ``` meson - void install_subdir(subdir_name) + void install_subdir(subdir_name, install_dir : ..., exclude_files : ..., exclude_directories : ...) ``` Installs the entire given subdirectory and its contents from the source tree to the location specified by the keyword argument `install_dir`. Note that due to implementation issues this command deletes the entire target dir before copying the files, so you should never use `install_subdir` to install into two overlapping directories (such as `foo` and `foo/bar`) because if you do the behavior is undefined. +The following keyword arguments are supported: + +- `install_dir`: the location to place the installed subdirectory. +- `exclude_files`: a list of file names that should not be installed. + Names are interpreted as paths relative to the `subdir_name` location. +- `exclude_directories`: a list of directory names that should not be installed. + Names are interpreted as paths relative to the `subdir_name` location. + ### is_variable() ``` meson diff --git a/docs/markdown/Release-notes-for-0.42.0.md b/docs/markdown/Release-notes-for-0.42.0.md index 0fa27c3d8..2335e5848 100644 --- a/docs/markdown/Release-notes-for-0.42.0.md +++ b/docs/markdown/Release-notes-for-0.42.0.md @@ -104,3 +104,9 @@ By default Meson adds the current source and build directories to the header search path. On some rare occasions this is not desired. Setting the `implicit_include_directories` keyword argument to `false` these directories are not used. + +## Allow excluding files or directories from `install_subdir` + +The [`install_subdir`](Reference-manual.md#install_subdir) command accepts the +new `exclude_files` and `exclude_directories` keyword arguments that allow +specified files or directories to be excluded from the installed subdirectory. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2b292b188..a0fd90a4d 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -813,7 +813,7 @@ int dummy; inst_dir = sd.installable_subdir src_dir = os.path.join(self.environment.get_source_dir(), subdir) dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir) - d.install_subdirs.append([src_dir, inst_dir, dst_dir, sd.install_mode]) + d.install_subdirs.append([src_dir, inst_dir, dst_dir, sd.install_mode, sd.exclude]) def generate_tests(self, outfile): self.serialize_tests() diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 2c1f8f1fb..970ab7cfe 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -456,12 +456,13 @@ class DataHolder(InterpreterObject): return self.held_object.install_dir class InstallDir(InterpreterObject): - def __init__(self, src_subdir, inst_subdir, install_dir, install_mode): + def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude): InterpreterObject.__init__(self) self.source_subdir = src_subdir self.installable_subdir = inst_subdir self.install_dir = install_dir self.install_mode = install_mode + self.exclude = exclude class Man(InterpreterObject): @@ -1297,7 +1298,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'install_data': {'install_dir', 'install_mode', 'sources'}, 'install_headers': {'install_dir', 'subdir'}, 'install_man': {'install_dir'}, - 'install_subdir': {'install_dir', 'install_mode'}, + 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode'}, 'jar': jar_kwargs, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, 'run_target': {'command', 'depends'}, @@ -2442,13 +2443,39 @@ class Interpreter(InterpreterBase): def func_install_subdir(self, node, args, kwargs): if len(args) != 1: raise InvalidArguments('Install_subdir requires exactly one argument.') + subdir = args[0] if 'install_dir' not in kwargs: raise InvalidArguments('Missing keyword argument install_dir') install_dir = kwargs['install_dir'] if not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') + if 'exclude_files' in kwargs: + exclude = kwargs['exclude_files'] + if not isinstance(exclude, list): + exclude = [exclude] + for f in exclude: + if not isinstance(f, str): + raise InvalidArguments('Exclude argument not a string.') + elif os.path.isabs(f): + raise InvalidArguments('Exclude argument cannot be absolute.') + exclude_files = {os.path.join(subdir, f) for f in exclude} + else: + exclude_files = set() + if 'exclude_directories' in kwargs: + exclude = kwargs['exclude_directories'] + if not isinstance(exclude, list): + exclude = [exclude] + for d in exclude: + if not isinstance(d, str): + raise InvalidArguments('Exclude argument not a string.') + elif os.path.isabs(d): + raise InvalidArguments('Exclude argument cannot be absolute.') + exclude_directories = {os.path.join(subdir, f) for f in exclude} + else: + exclude_directories = set() + exclude = (exclude_files, exclude_directories) install_mode = self._get_kwarg_install_mode(kwargs) - idir = InstallDir(self.subdir, args[0], install_dir, install_mode) + idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude) self.build.install_dirs.append(idir) return idir diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index f61cbab12..01ea771a3 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -128,17 +128,25 @@ def do_copyfile(from_file, to_file): restore_selinux_context(to_file) append_to_log(to_file) -def do_copydir(data, src_prefix, src_dir, dst_dir): +def do_copydir(data, src_prefix, src_dir, dst_dir, exclude): ''' Copies the directory @src_prefix (full path) into @dst_dir @src_dir is simply the parent directory of @src_prefix ''' + if exclude is not None: + exclude_files, exclude_dirs = exclude + else: + exclude_files = exclude_dirs = set() for root, dirs, files in os.walk(src_prefix): - for d in dirs: + for d in dirs[:]: abs_src = os.path.join(src_dir, root, d) filepart = abs_src[len(src_dir) + 1:] abs_dst = os.path.join(dst_dir, filepart) + # Remove these so they aren't visited by os.walk at all. + if filepart in exclude_dirs: + dirs.remove(d) + continue if os.path.isdir(abs_dst): continue if os.path.exists(abs_dst): @@ -149,6 +157,8 @@ def do_copydir(data, src_prefix, src_dir, dst_dir): for f in files: abs_src = os.path.join(src_dir, root, f) filepart = abs_src[len(src_dir) + 1:] + if filepart in exclude_files: + continue abs_dst = os.path.join(dst_dir, filepart) if os.path.isdir(abs_dst): print('Tried to copy file %s but a directory of that name already exists.' % abs_dst) @@ -184,14 +194,14 @@ def do_install(datafilename): run_install_script(d) def install_subdirs(d): - for (src_dir, inst_dir, dst_dir, mode) in d.install_subdirs: + for (src_dir, inst_dir, dst_dir, mode, exclude) in d.install_subdirs: if src_dir.endswith('/') or src_dir.endswith('\\'): src_dir = src_dir[:-1] 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) d.dirmaker.makedirs(dst_dir, exist_ok=True) - do_copydir(d, src_prefix, src_dir, dst_dir) + do_copydir(d, src_prefix, src_dir, dst_dir, exclude) dst_prefix = os.path.join(dst_dir, inst_dir) set_mode(dst_prefix, mode) @@ -317,7 +327,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(d, fname, os.path.dirname(fname), outdir) + do_copydir(d, fname, os.path.dirname(fname), outdir, None) else: raise RuntimeError('Unknown file type for {!r}'.format(fname)) printed_symlink_error = False diff --git a/test cases/common/66 install subdir/installed_files.txt b/test cases/common/66 install subdir/installed_files.txt index a610c510a..e5d230726 100644 --- a/test cases/common/66 install subdir/installed_files.txt +++ b/test cases/common/66 install subdir/installed_files.txt @@ -2,3 +2,5 @@ usr/share/sub1/data1.dat usr/share/sub1/second.dat usr/share/sub1/third.dat usr/share/sub1/sub2/data2.dat +usr/share/sub2/one.dat +usr/share/sub2/dircheck/excluded-three.dat diff --git a/test cases/common/66 install subdir/meson.build b/test cases/common/66 install subdir/meson.build index fed2f8909..18e0eafb8 100644 --- a/test cases/common/66 install subdir/meson.build +++ b/test cases/common/66 install subdir/meson.build @@ -1,5 +1,11 @@ project('install a whole subdir', 'c') +# A subdir with an exclusion: +install_subdir('sub2', + exclude_files : ['excluded-three.dat'], + exclude_directories : ['excluded'], + install_dir : 'share') + subdir('subdir') # A subdir with write perms only for the owner # and read-list perms for owner and group diff --git a/test cases/common/66 install subdir/sub2/dircheck/excluded-three.dat b/test cases/common/66 install subdir/sub2/dircheck/excluded-three.dat new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/common/66 install subdir/sub2/excluded-three.dat b/test cases/common/66 install subdir/sub2/excluded-three.dat new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/common/66 install subdir/sub2/excluded/two.dat b/test cases/common/66 install subdir/sub2/excluded/two.dat new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/common/66 install subdir/sub2/one.dat b/test cases/common/66 install subdir/sub2/one.dat new file mode 100644 index 000000000..e69de29bb