summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipe Brandenburger <filbranden@google.com>2018-04-24 21:39:59 -0700
committerNirbheek Chauhan <nirbheek.chauhan@gmail.com>2018-06-02 04:50:32 +0000
commit05c43cdcd19db98d53d5c9f1b50028d881471c2f (patch)
tree233a3a010cc132e4ce5cc26deb95afcd12243781
parent0ccc0e92d114a281ba99fd668f36e69670a0af68 (diff)
downloadmeson-05c43cdcd19db98d53d5c9f1b50028d881471c2f.tar.gz
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.
-rw-r--r--mesonbuild/backend/ninjabackend.py15
-rw-r--r--mesonbuild/build.py11
-rw-r--r--mesonbuild/interpreter.py25
-rw-r--r--mesonbuild/scripts/meson_install.py24
4 files changed, 51 insertions, 24 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index c7e194e67..1aff06f98 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.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):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 0bcb98fb2..1e7f5fe02 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -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
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 93fd237b6..b596ddc2d 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -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)
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index b51a351ed..eaa992b58 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -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