Add 'install_mode' to all installable targets

This makes it possible to customize permissions of all installable
targets, such as executable(), libraries, man pages, header files and
custom or generated targets.

This is useful, for instance, to install setuid/setgid binaries, which
was hard to accomplish without access to this attribute.
pull/3134/head
Filipe Brandenburger 7 years ago committed by Nirbheek Chauhan
parent 0ccc0e92d1
commit 05c43cdcd1
  1. 15
      mesonbuild/backend/ninjabackend.py
  2. 11
      mesonbuild/build.py
  3. 25
      mesonbuild/interpreter.py
  4. 24
      mesonbuild/scripts/meson_install.py

@ -724,6 +724,7 @@ int dummy;
"Pass 'false' for outputs that should not be installed and 'true' for\n" \
'using the default installation directory for an output.'
raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs))
install_mode = t.get_custom_install_mode()
# Install the target output(s)
if isinstance(t, build.BuildTarget):
should_strip = self.get_option_for_target('strip', t)
@ -731,7 +732,7 @@ int dummy;
# Done separately because of strip/aliases/rpath
if outdirs[0] is not False:
i = [self.get_target_filename(t), outdirs[0],
t.get_aliases(), should_strip, t.install_rpath]
t.get_aliases(), should_strip, t.install_rpath, install_mode]
d.targets.append(i)
# On toolchains/platforms that use an import library for
# linking (separate from the shared library with all the
@ -749,7 +750,7 @@ int dummy;
implib_install_dir,
# It has no aliases, should not be stripped, and
# doesn't have an install_rpath
{}, False, '']
{}, False, '', install_mode]
d.targets.append(i)
# Install secondary outputs. Only used for Vala right now.
if num_outdirs > 1:
@ -758,7 +759,7 @@ int dummy;
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
d.targets.append([f, outdir, {}, False, None])
d.targets.append([f, outdir, {}, False, None, install_mode])
elif isinstance(t, build.CustomTarget):
# If only one install_dir is specified, assume that all
# outputs will be installed into it. This is for
@ -770,14 +771,14 @@ int dummy;
if num_outdirs == 1 and num_out > 1:
for output in t.get_outputs():
f = os.path.join(self.get_target_dir(t), output)
d.targets.append([f, outdirs[0], {}, False, None])
d.targets.append([f, outdirs[0], {}, False, None, install_mode])
else:
for output, outdir in zip(t.get_outputs(), outdirs):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
d.targets.append([f, outdir, {}, False, None])
d.targets.append([f, outdir, {}, False, None, install_mode])
def generate_custom_install_script(self, d):
result = []
@ -809,7 +810,7 @@ int dummy;
msg = 'Invalid header type {!r} can\'t be installed'
raise MesonException(msg.format(f))
abspath = f.absolute_path(srcdir, builddir)
i = [abspath, outdir]
i = [abspath, outdir, h.get_custom_install_mode()]
d.headers.append(i)
def generate_man_install(self, d):
@ -823,7 +824,7 @@ int dummy;
subdir = os.path.join(manroot, 'man' + num)
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
dstabs = os.path.join(subdir, os.path.basename(f.fname) + '.gz')
i = [srcabs, dstabs]
i = [srcabs, dstabs, m.get_custom_install_mode()]
d.man.append(i)
def generate_data_install(self, d):

@ -64,6 +64,7 @@ buildtarget_kwargs = set([
'install',
'install_rpath',
'install_dir',
'install_mode',
'name_prefix',
'name_suffix',
'native',
@ -668,6 +669,9 @@ class BuildTarget(Target):
def get_custom_install_dir(self):
return self.install_dir
def get_custom_install_mode(self):
return self.install_mode
def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs)
self.copy_kwargs(kwargs)
@ -745,6 +749,7 @@ This will become a hard error in a future Meson release.''')
# the list index of that item will not be installed
self.install_dir = typeslistify(kwargs.get('install_dir', [None]),
(str, bool))
self.install_mode = kwargs.get('install_mode', None)
main_class = kwargs.get('main_class', '')
if not isinstance(main_class, str):
raise InvalidArguments('Main class must be a string')
@ -1626,6 +1631,7 @@ class CustomTarget(Target):
'capture',
'install',
'install_dir',
'install_mode',
'build_always',
'depends',
'depend_files',
@ -1774,9 +1780,11 @@ class CustomTarget(Target):
# If an item in this list is False, the output corresponding to
# the list index of that item will not be installed
self.install_dir = typeslistify(kwargs['install_dir'], (str, bool))
self.install_mode = kwargs.get('install_mode', None)
else:
self.install = False
self.install_dir = [None]
self.install_mode = None
self.build_always = kwargs.get('build_always', False)
if not isinstance(self.build_always, bool):
raise InvalidArguments('Argument build_always must be a boolean.')
@ -1803,6 +1811,9 @@ class CustomTarget(Target):
def get_custom_install_dir(self):
return self.install_dir
def get_custom_install_mode(self):
return self.install_mode
def get_outputs(self):
return self.outputs

@ -577,6 +577,7 @@ class Headers(InterpreterObject):
self.sources = sources
self.install_subdir = kwargs.get('subdir', '')
self.custom_install_dir = kwargs.get('install_dir', None)
self.custom_install_mode = kwargs.get('install_mode', None)
if self.custom_install_dir is not None:
if not isinstance(self.custom_install_dir, str):
raise InterpreterException('Custom_install_dir must be a string.')
@ -593,6 +594,9 @@ class Headers(InterpreterObject):
def get_custom_install_dir(self):
return self.custom_install_dir
def get_custom_install_mode(self):
return self.custom_install_mode
class DataHolder(InterpreterObject, ObjectHolder):
def __init__(self, data):
InterpreterObject.__init__(self)
@ -624,6 +628,7 @@ class Man(InterpreterObject):
self.sources = sources
self.validate_sources()
self.custom_install_dir = kwargs.get('install_dir', None)
self.custom_install_mode = kwargs.get('install_mode', None)
if self.custom_install_dir is not None and not isinstance(self.custom_install_dir, str):
raise InterpreterException('Custom_install_dir must be a string.')
@ -639,6 +644,9 @@ class Man(InterpreterObject):
def get_custom_install_dir(self):
return self.custom_install_dir
def get_custom_install_mode(self):
return self.custom_install_mode
def get_sources(self):
return self.sources
@ -1716,8 +1724,8 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
'build_target': known_build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'capture', 'install', 'format', 'output_format'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'install_mode', 'capture', 'install', 'format', 'output_format'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'install_mode', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version', 'private_headers'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
'executable': build.known_exe_kwargs,
@ -1725,8 +1733,8 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
'include_directories': {'is_system'},
'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
'install_headers': {'install_dir', 'subdir'},
'install_man': {'install_dir'},
'install_headers': {'install_dir', 'install_mode', 'subdir'},
'install_man': {'install_dir', 'install_mode'},
'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'},
'jar': build.known_jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
@ -2932,6 +2940,7 @@ root and issuing %s.
if len(args) != 1:
raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name')
name = args[0]
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs), self)
self.add_target(name, tg.held_object)
return tg
@ -3058,6 +3067,7 @@ root and issuing %s.
@permittedKwargs(permitted_kwargs['install_headers'])
def func_install_headers(self, node, args, kwargs):
source_files = self.source_strings_to_files(args)
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
h = Headers(source_files, kwargs)
self.build.headers.append(h)
return h
@ -3065,6 +3075,7 @@ root and issuing %s.
@permittedKwargs(permitted_kwargs['install_man'])
def func_install_man(self, node, args, kwargs):
fargs = self.source_strings_to_files(args)
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
m = Man(fargs, kwargs)
self.build.man.append(m)
return m
@ -3115,7 +3126,7 @@ root and issuing %s.
self.subdir = prev_subdir
def _get_kwarg_install_mode(self, kwargs):
if 'install_mode' not in kwargs:
if kwargs.get('install_mode', None) is None:
return None
install_mode = []
mode = mesonlib.typeslistify(kwargs.get('install_mode', []), (str, int))
@ -3358,7 +3369,8 @@ root and issuing %s.
idir = kwargs.get('install_dir', None)
if isinstance(idir, str) and idir:
cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
self.build.data.append(build.Data([cfile], idir))
install_mode = self._get_kwarg_install_mode(kwargs)
self.build.data.append(build.Data([cfile], idir, install_mode))
return mesonlib.File.from_built_file(self.subdir, output)
@permittedKwargs(permitted_kwargs['include_directories'])
@ -3642,6 +3654,7 @@ different subdirectory.
sources = self.source_strings_to_files(sources)
objs = extract_as_list(kwargs, 'objects')
kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies')
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
if 'extra_files' in kwargs:
ef = extract_as_list(kwargs, 'extra_files')
kwargs['extra_files'] = self.source_strings_to_files(ef)

@ -148,7 +148,7 @@ def do_copyfile(from_file, to_file):
selinux_updates.append(to_file)
append_to_log(to_file)
def do_copydir(data, src_dir, dst_dir, exclude):
def do_copydir(data, src_dir, dst_dir, exclude, install_mode):
'''
Copies the contents of directory @src_dir into @dst_dir.
@ -158,7 +158,7 @@ def do_copydir(data, src_dir, dst_dir, exclude):
excluded
foobar
file
do_copydir(..., '/foo', '/dst/dir', {'bar/excluded'}) creates
do_copydir(..., '/foo', '/dst/dir', {'bar/excluded'}, None) creates
/dst/
dir/
bar/
@ -170,6 +170,7 @@ def do_copydir(data, src_dir, dst_dir, exclude):
dst_dir: str, absolute path to the destination directory
exclude: (set(str), set(str)), tuple of (exclude_files, exclude_dirs),
each element of the set is a path relative to src_dir.
install_mode: FileMode object, or None to use defaults.
'''
if not os.path.isabs(src_dir):
raise ValueError('src_dir must be absolute, got %s' % src_dir)
@ -212,7 +213,7 @@ def do_copydir(data, src_dir, dst_dir, exclude):
os.mkdir(parent_dir)
shutil.copystat(os.path.dirname(abs_src), parent_dir)
shutil.copy2(abs_src, abs_dst, follow_symlinks=False)
sanitize_permissions(abs_dst, data.install_umask)
set_mode(abs_dst, install_mode, data.install_umask)
append_to_log(abs_dst)
def get_destdir_path(d, path):
@ -263,8 +264,7 @@ def install_subdirs(d):
full_dst_dir = get_destdir_path(d, dst_dir)
print('Installing subdir %s to %s' % (src_dir, full_dst_dir))
d.dirmaker.makedirs(full_dst_dir, exist_ok=True)
do_copydir(d, src_dir, full_dst_dir, exclude)
set_mode(full_dst_dir, mode, d.install_umask)
do_copydir(d, src_dir, full_dst_dir, exclude, mode)
def install_data(d):
for i in d.data:
@ -283,6 +283,7 @@ def install_man(d):
outfilename = get_destdir_path(d, m[1])
outdir = os.path.dirname(outfilename)
d.dirmaker.makedirs(outdir, exist_ok=True)
install_mode = m[2]
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:
@ -294,7 +295,7 @@ def install_man(d):
append_to_log(outfilename)
else:
do_copyfile(full_source_filename, outfilename)
sanitize_permissions(outfilename, d.install_umask)
set_mode(outfilename, install_mode, d.install_umask)
def install_headers(d):
for t in d.headers:
@ -302,10 +303,11 @@ def install_headers(d):
fname = os.path.basename(fullfilename)
outdir = get_destdir_path(d, t[1])
outfilename = os.path.join(outdir, fname)
install_mode = t[2]
print('Installing %s to %s' % (fname, outdir))
d.dirmaker.makedirs(outdir, exist_ok=True)
do_copyfile(fullfilename, outfilename)
sanitize_permissions(outfilename, d.install_umask)
set_mode(outfilename, install_mode, d.install_umask)
def run_install_script(d):
env = {'MESON_SOURCE_ROOT': d.source_dir,
@ -364,13 +366,14 @@ def install_targets(d):
aliases = t[2]
should_strip = t[3]
install_rpath = t[4]
install_mode = t[5]
print('Installing %s to %s' % (fname, outname))
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):
do_copyfile(fname, outname)
sanitize_permissions(outname, d.install_umask)
set_mode(outname, install_mode, d.install_umask)
if should_strip and d.strip_bin is not None:
if fname.endswith('.jar'):
print('Not stripping jar target:', os.path.basename(fname))
@ -387,12 +390,11 @@ def install_targets(d):
pdb_outname = os.path.splitext(outname)[0] + '.pdb'
print('Installing pdb file %s to %s' % (pdb_filename, pdb_outname))
do_copyfile(pdb_filename, pdb_outname)
sanitize_permissions(pdb_outname, d.install_umask)
set_mode(pdb_outname, install_mode, d.install_umask)
elif os.path.isdir(fname):
fname = os.path.join(d.build_dir, fname.rstrip('/'))
outname = os.path.join(outdir, os.path.basename(fname))
do_copydir(d, fname, outname, None)
sanitize_permissions(outname, d.install_umask)
do_copydir(d, fname, outname, None, install_mode)
else:
raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False

Loading…
Cancel
Save