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.
pull/1968/head
Elliott Sales de Andrade 8 years ago
parent 58bd1e83b4
commit 5cb1d00537
  1. 10
      docs/markdown/Reference-manual.md
  2. 6
      docs/markdown/Release-notes-for-0.42.0.md
  3. 2
      mesonbuild/backend/ninjabackend.py
  4. 33
      mesonbuild/interpreter.py
  5. 20
      mesonbuild/scripts/meson_install.py
  6. 2
      test cases/common/66 install subdir/installed_files.txt
  7. 6
      test cases/common/66 install subdir/meson.build
  8. 0
      test cases/common/66 install subdir/sub2/dircheck/excluded-three.dat
  9. 0
      test cases/common/66 install subdir/sub2/excluded-three.dat
  10. 0
      test cases/common/66 install subdir/sub2/excluded/two.dat
  11. 0
      test cases/common/66 install subdir/sub2/one.dat

@ -474,11 +474,19 @@ Installs the specified man files from the source tree into system's man director
### install_subdir() ### install_subdir()
``` meson ``` 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. 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() ### is_variable()
``` meson ``` meson

@ -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 header search path. On some rare occasions this is not desired. Setting
the `implicit_include_directories` keyword argument to `false` these the `implicit_include_directories` keyword argument to `false` these
directories are not used. 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.

@ -813,7 +813,7 @@ int dummy;
inst_dir = sd.installable_subdir inst_dir = sd.installable_subdir
src_dir = os.path.join(self.environment.get_source_dir(), subdir) src_dir = os.path.join(self.environment.get_source_dir(), subdir)
dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir) 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): def generate_tests(self, outfile):
self.serialize_tests() self.serialize_tests()

@ -456,12 +456,13 @@ class DataHolder(InterpreterObject):
return self.held_object.install_dir return self.held_object.install_dir
class InstallDir(InterpreterObject): 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) InterpreterObject.__init__(self)
self.source_subdir = src_subdir self.source_subdir = src_subdir
self.installable_subdir = inst_subdir self.installable_subdir = inst_subdir
self.install_dir = install_dir self.install_dir = install_dir
self.install_mode = install_mode self.install_mode = install_mode
self.exclude = exclude
class Man(InterpreterObject): class Man(InterpreterObject):
@ -1297,7 +1298,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'install_data': {'install_dir', 'install_mode', 'sources'}, 'install_data': {'install_dir', 'install_mode', 'sources'},
'install_headers': {'install_dir', 'subdir'}, 'install_headers': {'install_dir', 'subdir'},
'install_man': {'install_dir'}, 'install_man': {'install_dir'},
'install_subdir': {'install_dir', 'install_mode'}, 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode'},
'jar': jar_kwargs, 'jar': jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
'run_target': {'command', 'depends'}, 'run_target': {'command', 'depends'},
@ -2442,13 +2443,39 @@ class Interpreter(InterpreterBase):
def func_install_subdir(self, node, args, kwargs): def func_install_subdir(self, node, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise InvalidArguments('Install_subdir requires exactly one argument.') raise InvalidArguments('Install_subdir requires exactly one argument.')
subdir = args[0]
if 'install_dir' not in kwargs: if 'install_dir' not in kwargs:
raise InvalidArguments('Missing keyword argument install_dir') raise InvalidArguments('Missing keyword argument install_dir')
install_dir = kwargs['install_dir'] install_dir = kwargs['install_dir']
if not isinstance(install_dir, str): if not isinstance(install_dir, str):
raise InvalidArguments('Keyword argument install_dir not a string.') 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) 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) self.build.install_dirs.append(idir)
return idir return idir

@ -128,17 +128,25 @@ def do_copyfile(from_file, to_file):
restore_selinux_context(to_file) restore_selinux_context(to_file)
append_to_log(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 Copies the directory @src_prefix (full path) into @dst_dir
@src_dir is simply the parent directory of @src_prefix @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 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) abs_src = os.path.join(src_dir, root, d)
filepart = abs_src[len(src_dir) + 1:] filepart = abs_src[len(src_dir) + 1:]
abs_dst = os.path.join(dst_dir, filepart) 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): if os.path.isdir(abs_dst):
continue continue
if os.path.exists(abs_dst): if os.path.exists(abs_dst):
@ -149,6 +157,8 @@ def do_copydir(data, src_prefix, src_dir, dst_dir):
for f in files: for f in files:
abs_src = os.path.join(src_dir, root, f) abs_src = os.path.join(src_dir, root, f)
filepart = abs_src[len(src_dir) + 1:] filepart = abs_src[len(src_dir) + 1:]
if filepart in exclude_files:
continue
abs_dst = os.path.join(dst_dir, filepart) abs_dst = os.path.join(dst_dir, filepart)
if os.path.isdir(abs_dst): if os.path.isdir(abs_dst):
print('Tried to copy file %s but a directory of that name already exists.' % 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) run_install_script(d)
def install_subdirs(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('\\'): if src_dir.endswith('/') or src_dir.endswith('\\'):
src_dir = src_dir[:-1] src_dir = src_dir[:-1]
src_prefix = os.path.join(src_dir, inst_dir) src_prefix = os.path.join(src_dir, inst_dir)
print('Installing subdir %s to %s' % (src_prefix, dst_dir)) print('Installing subdir %s to %s' % (src_prefix, dst_dir))
dst_dir = get_destdir_path(d, dst_dir) dst_dir = get_destdir_path(d, dst_dir)
d.dirmaker.makedirs(dst_dir, exist_ok=True) 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) dst_prefix = os.path.join(dst_dir, inst_dir)
set_mode(dst_prefix, mode) set_mode(dst_prefix, mode)
@ -317,7 +327,7 @@ def install_targets(d):
do_copyfile(pdb_filename, pdb_outname) do_copyfile(pdb_filename, pdb_outname)
elif os.path.isdir(fname): elif os.path.isdir(fname):
fname = os.path.join(d.build_dir, fname.rstrip('/')) 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: else:
raise RuntimeError('Unknown file type for {!r}'.format(fname)) raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False printed_symlink_error = False

@ -2,3 +2,5 @@ usr/share/sub1/data1.dat
usr/share/sub1/second.dat usr/share/sub1/second.dat
usr/share/sub1/third.dat usr/share/sub1/third.dat
usr/share/sub1/sub2/data2.dat usr/share/sub1/sub2/data2.dat
usr/share/sub2/one.dat
usr/share/sub2/dircheck/excluded-three.dat

@ -1,5 +1,11 @@
project('install a whole subdir', 'c') 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') subdir('subdir')
# A subdir with write perms only for the owner # A subdir with write perms only for the owner
# and read-list perms for owner and group # and read-list perms for owner and group

Loading…
Cancel
Save