Add new builtin option --install-umask

This option controls the permissions of installed files (except for
those specified explicitly using install_mode option, e.g. in
install_data rules.)

An install-umask of 022 will install all binaries, directories and
executable files with mode rwxr-xr-x, while all data and non-executable
files will be installed with mode rw-r--r--.

Setting install-umask to the string 'preserve' will disable this
feature, keeping the permissions of installed files same as the files in
the build tree (or source tree for install_data and install_subdir.)
Note that, in this case, the umask used when building and that used when
checking out the source tree will leak into the install tree.

Keep the default as 'preserve', to show that no behavior is changed and
all tests keep passing unchanged.

Tested: ./run_tests.py
pull/3225/head
Filipe Brandenburger 7 years ago
parent a52543cd1e
commit 8651d55c6a
  1. 4
      mesonbuild/backend/backends.py
  2. 4
      mesonbuild/backend/ninjabackend.py
  3. 25
      mesonbuild/coredata.py
  4. 41
      mesonbuild/scripts/meson_install.py

@ -37,11 +37,13 @@ class CleanTrees:
self.trees = trees
class InstallData:
def __init__(self, source_dir, build_dir, prefix, strip_bin, mesonintrospect):
def __init__(self, source_dir, build_dir, prefix, strip_bin,
install_umask, mesonintrospect):
self.source_dir = source_dir
self.build_dir = build_dir
self.prefix = prefix
self.strip_bin = strip_bin
self.install_umask = install_umask
self.targets = []
self.headers = []
self.man = []

@ -668,7 +668,9 @@ int dummy;
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
strip_bin, self.environment.get_build_command() + ['introspect'])
strip_bin,
self.environment.coredata.get_builtin_option('install_umask'),
self.environment.get_build_command() + ['introspect'])
elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY')
elem.add_dep('all')
elem.add_item('DESC', 'Installing files.')

@ -105,6 +105,22 @@ class UserIntegerOption(UserOption):
except ValueError:
raise MesonException('Value string "%s" is not convertable to an integer.' % valuestring)
class UserUmaskOption(UserIntegerOption):
def __init__(self, name, description, value, yielding=None):
super().__init__(name, description, 0, 0o777, value, yielding)
def set_value(self, newvalue):
if newvalue is None or newvalue == 'preserve':
self.value = None
else:
super().set_value(newvalue)
def toint(self, valuestring):
try:
return int(valuestring, 8)
except ValueError as e:
raise MesonException('Invalid mode: {}'.format(e))
class UserComboOption(UserOption):
def __init__(self, name, description, choices, value, yielding=None):
super().__init__(name, description, choices, yielding)
@ -345,12 +361,12 @@ def is_builtin_option(optname):
def get_builtin_option_choices(optname):
if is_builtin_option(optname):
if builtin_options[optname][0] == UserStringOption:
return None
if builtin_options[optname][0] == UserComboOption:
return builtin_options[optname][2]
elif builtin_options[optname][0] == UserBooleanOption:
return [True, False]
else:
return builtin_options[optname][2]
return None
else:
raise RuntimeError('Tried to get the supported values for an unknown builtin option \'%s\'.' % optname)
@ -379,6 +395,8 @@ def get_builtin_option_default(optname, prefix='', noneIfSuppress=False):
o = builtin_options[optname]
if o[0] == UserComboOption:
return o[3]
if o[0] == UserIntegerOption:
return o[4]
if optname in builtin_dir_noprefix_options:
if noneIfSuppress:
# Return None if argparse defaulting should be suppressed for
@ -438,6 +456,7 @@ builtin_options = {
'backend': [UserComboOption, 'Backend to use.', backendlist, 'ninja'],
'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs.', True],
'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True],
'install_umask': [UserUmaskOption, 'Default umask to apply on permissions of installed files.', None],
}
# Special prefix-dependent defaults for installation directories that reside in

@ -51,12 +51,25 @@ class DirMaker:
for d in self.dirs:
append_to_log(d)
def set_mode(path, mode):
if mode is None:
# Keep mode unchanged
def is_executable(path):
'''Checks whether any of the "x" bits are set in the source file mode.'''
return bool(os.stat(path).st_mode & 0o111)
def sanitize_permissions(path, umask):
if umask is None:
return
if (mode.perms_s or mode.owner or mode.group) is None:
# Nothing to set
new_perms = 0o777 if is_executable(path) else 0o666
new_perms &= ~umask
try:
os.chmod(path, new_perms)
except PermissionError as e:
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...'
print(msg.format(path, new_perms, e.strerror))
def set_mode(path, mode, default_umask):
if mode is None or (mode.perms_s or mode.owner or mode.group) is None:
# Just sanitize permissions with the default umask
sanitize_permissions(path, default_umask)
return
# No chown() on Windows, and must set one of owner/group
if not is_windows() and (mode.owner or mode.group) is not None:
@ -83,6 +96,8 @@ def set_mode(path, mode):
except PermissionError as e:
msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...'
print(msg.format(path, mode.perms_s, e.strerror))
else:
sanitize_permissions(path, default_umask)
def restore_selinux_contexts():
'''
@ -180,6 +195,7 @@ def do_copydir(data, src_dir, dst_dir, exclude):
sys.exit(1)
data.dirmaker.makedirs(abs_dst)
shutil.copystat(abs_src, abs_dst)
sanitize_permissions(abs_dst, data.install_umask)
for f in files:
abs_src = os.path.join(root, f)
filepart = os.path.relpath(abs_src, start=src_dir)
@ -195,6 +211,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)
append_to_log(abs_dst)
def get_destdir_path(d, path):
@ -210,6 +227,8 @@ def do_install(datafilename):
d.destdir = os.environ.get('DESTDIR', '')
d.fullprefix = destdir_join(d.destdir, d.prefix)
if d.install_umask is not None:
os.umask(d.install_umask)
d.dirmaker = DirMaker()
with d.dirmaker:
install_subdirs(d) # Must be first, because it needs to delete the old subtree.
@ -226,7 +245,7 @@ def install_subdirs(d):
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)
set_mode(full_dst_dir, mode, d.install_umask)
def install_data(d):
for i in d.data:
@ -237,7 +256,7 @@ def install_data(d):
d.dirmaker.makedirs(outdir, exist_ok=True)
print('Installing %s to %s' % (fullfilename, outdir))
do_copyfile(fullfilename, outfilename)
set_mode(outfilename, mode)
set_mode(outfilename, mode, d.install_umask)
def install_man(d):
for m in d.man:
@ -256,6 +275,7 @@ def install_man(d):
append_to_log(outfilename)
else:
do_copyfile(full_source_filename, outfilename)
sanitize_permissions(outfilename, d.install_umask)
def install_headers(d):
for t in d.headers:
@ -266,6 +286,7 @@ def install_headers(d):
print('Installing %s to %s' % (fname, outdir))
d.dirmaker.makedirs(outdir, exist_ok=True)
do_copyfile(fullfilename, outfilename)
sanitize_permissions(outfilename, d.install_umask)
def run_install_script(d):
env = {'MESON_SOURCE_ROOT': d.source_dir,
@ -330,6 +351,7 @@ def install_targets(d):
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)
if should_strip and d.strip_bin is not None:
if fname.endswith('.jar'):
print('Not stripping jar target:', os.path.basename(fname))
@ -346,9 +368,12 @@ 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)
elif os.path.isdir(fname):
fname = os.path.join(d.build_dir, fname.rstrip('/'))
do_copydir(d, fname, os.path.join(outdir, os.path.basename(fname)), None)
outname = os.path.join(outdir, os.path.basename(fname))
do_copydir(d, fname, outname, None)
sanitize_permissions(outname, d.install_umask)
else:
raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False

Loading…
Cancel
Save