From 905ff356118d3317aa1922bbfee3dd4c3cb71a6f Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 14 Jan 2017 20:38:08 +0530 Subject: [PATCH 1/2] wx deps: Always set modversion, even if dep not found Fixes: Traceback (most recent call last): File "mesonbuild/mesonmain.py", line 295, in run app.generate() File "mesonbuild/mesonmain.py", line 177, in generate intr.run() File "mesonbuild/interpreter.py", line 2444, in run super().run() File "mesonbuild/interpreterbase.py", line 124, in run self.evaluate_codeblock(self.ast, start=1) File "mesonbuild/interpreterbase.py", line 145, in evaluate_codeblock raise e File "mesonbuild/interpreterbase.py", line 139, in evaluate_codeblock self.evaluate_statement(cur) File "mesonbuild/interpreterbase.py", line 152, in evaluate_statement return self.assignment(cur) File "mesonbuild/interpreterbase.py", line 546, in assignment value = self.evaluate_statement(node.value) File "mesonbuild/interpreterbase.py", line 150, in evaluate_statement return self.function_call(cur) File "mesonbuild/interpreterbase.py", line 371, in function_call return self.funcs[func_name](node, self.flatten(posargs), kwargs) File "mesonbuild/interpreter.py", line 1876, in func_dependency found = cached_dep.get_version() File "mesonbuild/dependencies.py", line 369, in get_version return self.modversion AttributeError: 'WxDependency' object has no attribute 'modversion' ninja: error: rebuilding 'build.ninja': subcommand failed --- mesonbuild/dependencies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 97aec7eac..fb576ea1b 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -316,6 +316,7 @@ class WxDependency(Dependency): def __init__(self, environment, kwargs): Dependency.__init__(self, 'wx') self.is_found = False + self.modversion = 'none' if WxDependency.wx_found is None: self.check_wxconfig() if not WxDependency.wx_found: From 7d6f628ed4c3c3dca32bef01b2581f2e9bcde189 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 14 Jan 2017 14:41:18 +0530 Subject: [PATCH 2/2] Support file perms for install_data and install_subdir With the 'install_mode' kwarg, you can now specify the file and directory permissions and the owner and the group to be used while installing. You can pass either: * A single string specifying just the permissions * A list of strings with: - The first argument a string of permissions - The second argument a string specifying the owner or an int specifying the uid - The third argument a string specifying the group or an int specifying the gid Specifying `false` as any of the arguments skips setting that one. The format of the permissions kwarg is the same as the symbolic notation used by ls -l with the first character that specifies 'd', '-', 'c', etc for the file type omitted since that is always obvious from the context. Includes unit tests for the same. Sadly these only run on Linux right now, but we want them to run on all platforms. We do set the mode in the integration tests for all platforms but we don't check if they were actually set correctly. --- mesonbuild/backend/backends.py | 3 +- mesonbuild/backend/ninjabackend.py | 4 +- mesonbuild/build.py | 3 +- mesonbuild/interpreter.py | 36 +++++- mesonbuild/mesonlib.py | 104 +++++++++++++++++- mesonbuild/scripts/meson_install.py | 32 +++++- run_unittests.py | 89 +++++++++++++++ test cases/common/12 data/installed_files.txt | 1 + test cases/common/12 data/meson.build | 11 +- test cases/common/12 data/runscript.sh | 3 + .../common/66 install subdir/meson.build | 4 +- .../66 install subdir/subdir/meson.build | 4 +- 12 files changed, 277 insertions(+), 17 deletions(-) create mode 100644 test cases/common/12 data/runscript.sh diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index e46c2c5df..6f8a50eee 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -459,7 +459,8 @@ class Backend: mfobj['projects'] = self.build.dep_manifest with open(ifilename, 'w') as f: f.write(json.dumps(mfobj)) - d.data.append([ifilename, ofilename]) + # Copy file from, to, and with mode unchanged + d.data.append([ifilename, ofilename, None]) def get_regen_filelist(self): '''List of all files whose alteration means that the build diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e1a478cef..628718f1e 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -700,7 +700,7 @@ int dummy; assert(isinstance(f, mesonlib.File)) plain_f = os.path.split(f.fname)[1] dstabs = os.path.join(subdir, plain_f) - i = [f.absolute_path(srcdir, builddir), dstabs] + i = [f.absolute_path(srcdir, builddir), dstabs, de.install_mode] d.data.append(i) def generate_subdir_install(self, d): @@ -715,7 +715,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]) + d.install_subdirs.append([src_dir, inst_dir, dst_dir, sd.install_mode]) def generate_tests(self, outfile): self.serialise_tests() diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ceae49bb5..dc072a5e6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1502,9 +1502,10 @@ class ConfigurationData: # A bit poorly named, but this represents plain data files to copy # during install. class Data: - def __init__(self, sources, install_dir): + def __init__(self, sources, install_dir, install_mode=None): self.sources = sources self.install_dir = install_dir + self.install_mode = install_mode if not isinstance(self.sources, list): self.sources = [self.sources] for s in self.sources: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index b5cd0b6dc..cb5b6175d 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -22,7 +22,7 @@ from . import optinterpreter from . import compilers from .wrap import wrap from . import mesonlib -from .mesonlib import Popen_safe +from .mesonlib import FileMode, Popen_safe from .dependencies import InternalDependency, Dependency from .interpreterbase import InterpreterBase from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs @@ -453,11 +453,12 @@ class DataHolder(InterpreterObject): return self.held_object.install_dir class InstallDir(InterpreterObject): - def __init__(self, source_subdir, installable_subdir, install_dir): + def __init__(self, src_subdir, inst_subdir, install_dir, install_mode): InterpreterObject.__init__(self) - self.source_subdir = source_subdir - self.installable_subdir = installable_subdir + self.source_subdir = src_subdir + self.installable_subdir = inst_subdir self.install_dir = install_dir + self.install_mode = install_mode class Man(InterpreterObject): @@ -2141,6 +2142,25 @@ requirements use the version keyword argument instead.''') self.evaluate_codeblock(codeblock) self.subdir = prev_subdir + def _get_kwarg_install_mode(self, kwargs): + if 'install_mode' not in kwargs: + return None + install_mode = [] + mode = mesonlib.stringintlistify(kwargs.get('install_mode', [])) + for m in mode: + # We skip any arguments that are set to `false` + if m is False: + m = None + install_mode.append(m) + if len(install_mode) > 3: + raise InvalidArguments('Keyword argument install_mode takes at ' + 'most 3 arguments.') + if len(install_mode) > 0 and install_mode[0] is not None and \ + not isinstance(install_mode[0], str): + raise InvalidArguments('Keyword argument install_mode requires the ' + 'permissions arg to be a string or false') + return FileMode(*install_mode) + def func_install_data(self, node, args, kwargs): kwsource = mesonlib.stringlistify(kwargs.get('sources', [])) raw_sources = args + kwsource @@ -2153,7 +2173,10 @@ requirements use the version keyword argument instead.''') source_strings.append(s) sources += self.source_strings_to_files(source_strings) install_dir = kwargs.get('install_dir', None) - data = DataHolder(build.Data(sources, install_dir)) + if not isinstance(install_dir, (str, type(None))): + raise InvalidArguments('Keyword argument install_dir not a string.') + install_mode = self._get_kwarg_install_mode(kwargs) + data = DataHolder(build.Data(sources, install_dir, install_mode)) self.build.data.append(data.held_object) return data @@ -2166,7 +2189,8 @@ requirements use the version keyword argument instead.''') install_dir = kwargs['install_dir'] if not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') - idir = InstallDir(self.subdir, args[0], install_dir) + install_mode = self._get_kwarg_install_mode(kwargs) + idir = InstallDir(self.subdir, args[0], install_dir, install_mode) self.build.install_dirs.append(idir) return idir diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 2587d6f5b..2ad43c823 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -14,6 +14,7 @@ """A library of random helper functionality.""" +import stat import platform, subprocess, operator, os, shutil, re from glob import glob @@ -24,6 +25,97 @@ class MesonException(Exception): class EnvironmentException(MesonException): '''Exceptions thrown while processing and creating the build environment''' +class FileMode: + # The first triad is for owner permissions, the second for group permissions, + # and the third for others (everyone else). + # For the 1st character: + # 'r' means can read + # '-' means not allowed + # For the 2nd character: + # 'w' means can write + # '-' means not allowed + # For the 3rd character: + # 'x' means can execute + # 's' means can execute and setuid/setgid is set (owner/group triads only) + # 'S' means cannot execute and setuid/setgid is set (owner/group triads only) + # 't' means can execute and sticky bit is set ("others" triads only) + # 'T' means cannot execute and sticky bit is set ("others" triads only) + # '-' means none of these are allowed + # + # The meanings of 'rwx' perms is not obvious for directories; see: + # https://www.hackinglinuxexposed.com/articles/20030424.html + # + # For information on this notation such as setuid/setgid/sticky bits, see: + # https://en.wikipedia.org/wiki/File_system_permissions#Symbolic_notation + symbolic_perms_regex = re.compile('[r-][w-][xsS-]' # Owner perms + '[r-][w-][xsS-]' # Group perms + '[r-][w-][xtT-]') # Others perms + + def __init__(self, perms=None, owner=None, group=None): + self.perms_s = perms + self.perms = self.perms_s_to_bits(perms) + self.owner = owner + self.group = group + + def __repr__(self): + ret = '