From a4e1be4e5b2430ec5ef2e88a9b90de07199f8e14 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 13 Jan 2021 13:24:26 -0800 Subject: scripts/depfixer: make rpaths_dirs_to_remove a set It's only used for doing an `if x in container` check, which will be faster with a set, and the only caller already has a set, so avoid we can avoid a type conversion as well. --- mesonbuild/scripts/depfixer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index e31f87d..8ce74ee 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -294,13 +294,13 @@ class Elf(DataSizes): self.bf.seek(offset) self.bf.write(newname) - def fix_rpath(self, rpath_dirs_to_remove: T.List[bytes], new_rpath: bytes) -> None: + def fix_rpath(self, rpath_dirs_to_remove: T.Set[bytes], new_rpath: bytes) -> None: # The path to search for can be either rpath or runpath. # Fix both of them to be sure. self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RPATH) self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RUNPATH) - def fix_rpathtype_entry(self, rpath_dirs_to_remove: T.List[bytes], new_rpath: bytes, entrynum: int) -> None: + def fix_rpathtype_entry(self, rpath_dirs_to_remove: T.Set[bytes], new_rpath: bytes, entrynum: int) -> None: rp_off = self.get_entry_offset(entrynum) if rp_off is None: if self.verbose: @@ -365,7 +365,7 @@ class Elf(DataSizes): entry.write(self.bf) return None -def fix_elf(fname: str, rpath_dirs_to_remove: T.List[bytes], new_rpath: T.Optional[bytes], verbose: bool = True) -> None: +def fix_elf(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Optional[bytes], verbose: bool = True) -> None: with Elf(fname, verbose) as e: if new_rpath is None: e.print_rpath() @@ -452,7 +452,7 @@ def fix_jar(fname: str) -> None: f.truncate() subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF']) -def fix_rpath(fname: str, rpath_dirs_to_remove: T.List[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], verbose: bool = True) -> None: +def fix_rpath(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], verbose: bool = True) -> None: global INSTALL_NAME_TOOL # Static libraries, import libraries, debug information, headers, etc # never have rpaths -- cgit v1.1 From 6180992d4939f931aaeae74138dae069a60a3485 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 09:49:55 -0800 Subject: minstall: fix imports This uses PEP8 style, one per from `import mod`, but with commas from `from mod import a, b`. Also run sort, for niceness. --- mesonbuild/minstall.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 153ac34..4d40752 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -12,19 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys, pickle, os, shutil, subprocess, errno -import argparse -import shlex from glob import glob from pathlib import Path +import argparse +import errno +import os +import pickle +import shlex +import shutil +import subprocess +import sys from . import environment -from .scripts import depfixer -from .scripts import destdir_join -from .mesonlib import is_windows, Popen_safe from .backend.backends import InstallData from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version +from .mesonlib import is_windows, Popen_safe +from .scripts import depfixer, destdir_join try: from __main__ import __file__ as main_file except ImportError: -- cgit v1.1 From 022632c91b255739c5100601a02b197cb29cd23e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 10:50:22 -0800 Subject: build/interperter: Add annotations and move input validation to interpreter This moves the user input validation to the interpreter, instead of being in the build module, and adds type annotations. --- mesonbuild/build.py | 10 +++------- mesonbuild/interpreter.py | 19 +++++++++++++------ mesonbuild/modules/cmake.py | 4 ++-- mesonbuild/modules/gnome.py | 2 +- mesonbuild/modules/pkgconfig.py | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index dacf68b..736d42c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2567,19 +2567,15 @@ class ConfigurationData: # A bit poorly named, but this represents plain data files to copy # during install. class Data: - def __init__(self, sources, install_dir, install_mode=None, rename=None): + def __init__(self, sources: T.List[File], install_dir: str, + install_mode: T.Optional['FileMode'] = None, rename: T.List[str] = None): self.sources = sources self.install_dir = install_dir self.install_mode = install_mode - self.sources = listify(self.sources) - for s in self.sources: - assert(isinstance(s, File)) if rename is None: self.rename = [os.path.basename(f.fname) for f in self.sources] else: - self.rename = stringlistify(rename) - if len(self.rename) != len(self.sources): - raise MesonException('Size of rename argument is different from number of sources') + self.rename = rename class RunScript(dict): def __init__(self, script, args): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 9466fe4..fdb6fa7 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -4300,11 +4300,11 @@ This will become a hard error in the future.''' % kwargs['input'], location=self @FeatureNewKwargs('install_data', '0.46.0', ['rename']) @FeatureNewKwargs('install_data', '0.38.0', ['install_mode']) @permittedKwargs(permitted_kwargs['install_data']) - def func_install_data(self, node, args, kwargs): + def func_install_data(self, node, args: T.List, kwargs: T.Dict[str, T.Any]): kwsource = mesonlib.stringlistify(kwargs.get('sources', [])) raw_sources = args + kwsource - sources = [] - source_strings = [] + sources: T.List[mesonlib.File] = [] + source_strings: T.List[str] = [] for s in raw_sources: if isinstance(s, mesonlib.File): sources.append(s) @@ -4313,11 +4313,18 @@ This will become a hard error in the future.''' % kwargs['input'], location=self else: raise InvalidArguments('Argument must be string or file.') sources += self.source_strings_to_files(source_strings) - install_dir = kwargs.get('install_dir', None) - if not isinstance(install_dir, (str, type(None))): + install_dir: T.Optional[str] = kwargs.get('install_dir', None) + if install_dir is not None and not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') install_mode = self._get_kwarg_install_mode(kwargs) - rename = kwargs.get('rename', None) + rename: T.Optional[T.List[str]] = kwargs.get('rename', None) + if rename is not None: + rename = mesonlib.stringlistify(rename) + if len(rename) != len(sources): + raise InvalidArguments( + '"rename" and "sources" argument lists must be the same length if "rename" is given. ' + f'Rename has {len(rename)} elements and sources has {len(sources)}.') + data = DataHolder(build.Data(sources, install_dir, install_mode, rename)) self.build.data.append(data.held_object) return data diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index f6afaf3..ef53d9f 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -285,7 +285,7 @@ class CmakeModule(ExtensionModule): } mesonlib.do_conf_file(template_file, version_file, conf, 'meson') - res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), version_file), pkgroot) + res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), version_file)], pkgroot) return ModuleReturnValue(res, [res]) def create_package_file(self, infile, outfile, PACKAGE_RELATIVE_PATH, extra, confdata): @@ -370,7 +370,7 @@ class CmakeModule(ExtensionModule): if conffile not in interpreter.build_def_files: interpreter.build_def_files.append(conffile) - res = build.Data(mesonlib.File(True, ofile_path, ofile_fname), install_dir) + res = build.Data([mesonlib.File(True, ofile_path, ofile_fname)], install_dir) interpreter.build.data.append(res) return res diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 21570bd..5cad9f5 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1629,7 +1629,7 @@ G_END_DECLS''' with open(fname, 'w') as ofile: for package in packages: ofile.write(package + '\n') - return build.Data(mesonlib.File(True, outdir, fname), install_dir) + return build.Data([mesonlib.File(True, outdir, fname)], install_dir) def _get_vapi_link_with(self, target): link_with = [] diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 7d347a6..db589a3 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -539,7 +539,7 @@ class PkgConfigModule(ExtensionModule): self.generate_pkgconfig_file(state, deps, subdirs, name, description, url, version, pcfile, conflicts, variables, False, dataonly) - res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot) + res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot) variables = self.interpreter.extract_variables(kwargs, argname='uninstalled_variables', dict_new=True) variables = parse_variable_list(variables) -- cgit v1.1 From 59328aba2929b969a7e7ce1390d891dafe391a7f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 11:20:13 -0800 Subject: build: Add some type annotations --- mesonbuild/build.py | 30 ++++++++++++++++++------------ mesonbuild/minstall.py | 2 ++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 736d42c..23d2ff7 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -404,6 +404,9 @@ class EnvironmentVariables: return env class Target: + + # TODO: should Target be an abc.ABCMeta? + def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice): if has_path_sep(name): # Fix failing test 53 when this becomes an error. @@ -443,7 +446,10 @@ a hard error in the future.'''.format(name)) return NotImplemented return self.get_id() >= other.get_id() - def get_install_dir(self, environment): + def get_default_install_dir(self, env: environment.Environment) -> str: + raise NotImplementedError + + def get_install_dir(self, environment: environment.Environment) -> T.Tuple[T.Any, bool]: # Find the installation directory. default_install_dir = self.get_default_install_dir(environment) outdirs = self.get_custom_install_dir() @@ -554,7 +560,7 @@ class BuildTarget(Target): self.external_deps = [] self.include_dirs = [] self.link_language = kwargs.get('link_language') - self.link_targets = [] + self.link_targets: T.List[BuildTarget] = [] self.link_whole_targets = [] self.link_depends = [] self.added_deps = set() @@ -572,7 +578,7 @@ class BuildTarget(Target): self.pic = False self.pie = False # Track build_rpath entries so we can remove them at install time - self.rpath_dirs_to_remove = set() + self.rpath_dirs_to_remove: T.Set[bytes] = set() # Sources can be: # 1. Pre-existing source files in the source tree # 2. Pre-existing sources generated by configure_file in the build tree @@ -884,7 +890,7 @@ class BuildTarget(Target): result.update(i.get_link_dep_subdirs()) return result - def get_default_install_dir(self, environment): + def get_default_install_dir(self, environment: environment.Environment) -> str: return environment.get_libdir() def get_custom_install_dir(self): @@ -995,7 +1001,7 @@ This will become a hard error in a future Meson release.''') if not(os.path.isfile(trial)): raise InvalidArguments('Tried to add non-existing extra file {}.'.format(i)) self.extra_files = extra_files - self.install_rpath = kwargs.get('install_rpath', '') + self.install_rpath: str = kwargs.get('install_rpath', '') if not isinstance(self.install_rpath, str): raise InvalidArguments('Install_rpath is not a string.') self.build_rpath = kwargs.get('build_rpath', '') @@ -1299,7 +1305,7 @@ You probably should put it in link_with instead.''') else: self.extra_args[language] = args - def get_aliases(self): + def get_aliases(self) -> T.Dict[str, str]: return {} def get_langs_used_by_deps(self) -> T.List[str]: @@ -1673,7 +1679,7 @@ class Executable(BuildTarget): # Only linkwithable if using export_dynamic self.is_linkwithable = self.export_dynamic - def get_default_install_dir(self, environment): + def get_default_install_dir(self, environment: environment.Environment) -> str: return environment.get_bindir() def description(self): @@ -1802,8 +1808,8 @@ class SharedLibrary(BuildTarget): self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}' self.determine_filenames(environment) - def get_link_deps_mapping(self, prefix, environment): - result = {} + def get_link_deps_mapping(self, prefix, environment) -> T.Dict[str, str]: + result: T.Dict[str, str] = {} mappings = self.get_transitive_link_deps_mapping(prefix, environment) old = get_target_macos_dylib_install_name(self) if old not in mappings: @@ -2053,7 +2059,7 @@ class SharedLibrary(BuildTarget): def get_all_link_deps(self): return [self] + self.get_transitive_link_deps() - def get_aliases(self): + def get_aliases(self) -> T.Dict[str, str]: """ If the versioned library name is libfoo.so.0.100.0, aliases are: * libfoo.so.0 (soversion) -> libfoo.so.0.100.0 @@ -2061,11 +2067,11 @@ class SharedLibrary(BuildTarget): Same for dylib: * libfoo.dylib (unversioned; for linking) -> libfoo.0.dylib """ - aliases = {} + aliases: T.Dict[str, str] = {} # Aliases are only useful with .so and .dylib libraries. Also if # there's no self.soversion (no versioning), we don't need aliases. if self.suffix not in ('so', 'dylib') or not self.soversion: - return {} + return aliases # With .so libraries, the minor and micro versions are also in the # filename. If ltversion != soversion we create an soversion alias: # libfoo.so.0 -> libfoo.so.0.100.0 diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 4d40752..0adcda2 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -538,6 +538,7 @@ class Installer: else: raise + def rebuild_all(wd: str) -> bool: if not (Path(wd) / 'build.ninja').is_file(): print('Only ninja backend is supported to rebuild the project before installation.') @@ -555,6 +556,7 @@ def rebuild_all(wd: str) -> bool: return True + def run(opts): datafilename = 'meson-private/install.dat' private_dir = os.path.dirname(datafilename) -- cgit v1.1 From e36aca42d0980ce4b43e5753d798c986135e9f43 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 11:31:25 -0800 Subject: build/interpreter: Split InstallDir to fix layering violation Currently InstallDir is part of the interpreter, and is an Interpreter object, which is then put in the Build object. This is a layering violation, the interperter should have a Holder for build data. This patch fixes that. --- mesonbuild/build.py | 17 ++++++++- mesonbuild/interpreter.py | 40 ++++++++++------------ mesonbuild/modules/unstable_external_project.py | 18 +++++----- .../69 install_data rename bad size/test.json | 2 +- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 23d2ff7..017b0f0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -171,6 +171,21 @@ class Man: return self.sources +class InstallDir: + + def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str, + install_mode: T.Optional['FileMode'], + exclude: T.Tuple[T.Set[str], T.Set[str]], + strip_directory: bool, from_source_dir: bool = True): + self.source_subdir = src_subdir + self.installable_subdir = inst_subdir + self.install_dir = install_dir + self.install_mode = install_mode + self.exclude = exclude + self.strip_directory = strip_directory + self.from_source_dir = from_source_dir + + class Build: """A class that holds the status of one build including all dependencies and so on. @@ -198,7 +213,7 @@ class Build: self.install_scripts = [] self.postconf_scripts = [] self.dist_scripts = [] - self.install_dirs = [] + self.install_dirs: T.List[InstallDir] = [] self.dep_manifest_name = None self.dep_manifest = {} self.stdlibs = PerMachine({}, {}) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index fdb6fa7..15a6ddd 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -757,17 +757,11 @@ class DataHolder(InterpreterObject, ObjectHolder): def get_install_dir(self): return self.held_object.install_dir -class InstallDir(InterpreterObject): - def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, - exclude, strip_directory, from_source_dir=True): +class InstallDirHolder(InterpreterObject, ObjectHolder): + + def __init__(self, obj: build.InstallDir): InterpreterObject.__init__(self) - self.source_subdir = src_subdir - self.installable_subdir = inst_subdir - self.install_dir = install_dir - self.install_mode = install_mode - self.exclude = exclude - self.strip_directory = strip_directory - self.from_source_dir = from_source_dir + ObjectHolder.__init__(self, obj) class ManHolder(InterpreterObject, ObjectHolder): @@ -2591,8 +2585,8 @@ class Interpreter(InterpreterBase): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps self.process_new_values(v.sources[0]) - elif isinstance(v, InstallDir): - self.build.install_dirs.append(v) + elif isinstance(v, InstallDirHolder): + self.build.install_dirs.append(v.held_object) elif isinstance(v, Test): self.build.tests.append(v) elif hasattr(v, 'held_object'): @@ -4336,43 +4330,45 @@ This will become a hard error in the future.''' % kwargs['input'], location=self def func_install_subdir(self, node, args, kwargs): if len(args) != 1: raise InvalidArguments('Install_subdir requires exactly one argument.') - subdir = args[0] + subdir: str = args[0] + if not isinstance(subdir, str): + raise InvalidArguments('install_subdir positional argument 1 must be a string.') if 'install_dir' not in kwargs: raise InvalidArguments('Missing keyword argument install_dir') - install_dir = kwargs['install_dir'] + install_dir: str = kwargs['install_dir'] if not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') if 'strip_directory' in kwargs: - if not isinstance(kwargs['strip_directory'], bool): + strip_directory: bool = kwargs['strip_directory'] + if not isinstance(strip_directory, bool): raise InterpreterException('"strip_directory" keyword must be a boolean.') - strip_directory = kwargs['strip_directory'] else: strip_directory = False if 'exclude_files' in kwargs: - exclude = extract_as_list(kwargs, 'exclude_files') + exclude: T.List[str] = extract_as_list(kwargs, 'exclude_files') 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 = set(exclude) + exclude_files: T.Set[str] = set(exclude) else: exclude_files = set() if 'exclude_directories' in kwargs: - exclude = extract_as_list(kwargs, 'exclude_directories') + exclude: T.List[str] = extract_as_list(kwargs, 'exclude_directories') 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 = set(exclude) + exclude_directories: T.Set[str] = set(exclude) else: exclude_directories = set() exclude = (exclude_files, exclude_directories) install_mode = self._get_kwarg_install_mode(kwargs) - idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory) + idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory) self.build.install_dirs.append(idir) - return idir + return InstallDirHolder(idir) @FeatureNewKwargs('configure_file', '0.47.0', ['copy', 'output_format', 'install_mode', 'encoding']) @FeatureNewKwargs('configure_file', '0.46.0', ['format']) diff --git a/mesonbuild/modules/unstable_external_project.py b/mesonbuild/modules/unstable_external_project.py index 809b590..1604e25 100644 --- a/mesonbuild/modules/unstable_external_project.py +++ b/mesonbuild/modules/unstable_external_project.py @@ -22,7 +22,7 @@ from ..mesonlib import (MesonException, Popen_safe, MachineChoice, get_variable_regex, do_replacement) from ..interpreterbase import InterpreterObject, InterpreterException, FeatureNew from ..interpreterbase import stringArgs, permittedKwargs -from ..interpreter import Interpreter, DependencyHolder, InstallDir +from ..interpreter import Interpreter, DependencyHolder, InstallDirHolder from ..compilers.compilers import CFLAGS_MAPPING, CEXE_MAPPING from ..dependencies.base import InternalDependency, PkgConfigDependency from ..environment import Environment @@ -192,15 +192,15 @@ class ExternalProject(InterpreterObject): self.subproject, target_kwargs) - idir = InstallDir(self.subdir.as_posix(), - Path('dist', self.rel_prefix).as_posix(), - install_dir='.', - install_mode=None, - exclude=None, - strip_directory=True, - from_source_dir=False) + idir = build.InstallDir(self.subdir.as_posix(), + Path('dist', self.rel_prefix).as_posix(), + install_dir='.', + install_mode=None, + exclude=None, + strip_directory=True, + from_source_dir=False) - return [self.target, idir] + return [self.target, InstallDirHolder(idir)] @stringArgs @permittedKwargs({'subdir'}) diff --git a/test cases/failing/69 install_data rename bad size/test.json b/test cases/failing/69 install_data rename bad size/test.json index 1329fec..b99688a 100644 --- a/test cases/failing/69 install_data rename bad size/test.json +++ b/test cases/failing/69 install_data rename bad size/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/69 install_data rename bad size/meson.build:3:0: ERROR: Size of rename argument is different from number of sources" + "line": "test cases/failing/69 install_data rename bad size/meson.build:3:0: ERROR: \"rename\" and \"sources\" argument lists must be the same length if \"rename\" is given. Rename has 1 elements and sources has 2." } ] } -- cgit v1.1 From 96d7f6c7cd46a3f13b06e962fc18a8013b22082b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 11:42:47 -0800 Subject: backends/backends: Add type annotations for InstallData This adds enough type annotations for InstallData and friends to make minstall happy. There is also a small change in that I've replaced the List[List] with List[Tuple], as tuples are more appropraite data types for the information (fixed length, position matters, different types at different indexes) --- mesonbuild/backend/backends.py | 63 ++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 6dad189..92e6c15 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -39,7 +39,10 @@ if T.TYPE_CHECKING: from ..arglist import CompilerArgs from ..compilers import Compiler from ..interpreter import Interpreter, Test + from ..mesonlib import FileMode + InstallType = T.List[T.Tuple[str, str, T.Optional['FileMode']]] + InstallSubdirsType = T.List[T.Tuple[str, str, T.Optional['FileMode'], T.Tuple[T.Set[str], T.Set[str]]]] class TestProtocol(enum.Enum): @@ -80,26 +83,31 @@ class CleanTrees: self.trees = trees class InstallData: - def __init__(self, source_dir, build_dir, prefix, strip_bin, - install_umask, mesonintrospect, version): + def __init__(self, source_dir: str, build_dir: str, prefix: str, + strip_bin: T.List[str], install_umask: T.Union[str, int], + mesonintrospect: T.List[str], version: str): + # TODO: in python 3.8 or with typing_Extensions install_umask could be: + # `T.Union[T.Literal['preserve'], int]`, which would be more accurate. 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 = [] - self.data = [] - self.po_package_name = '' + self.targets: T.List[TargetInstallData] = [] + self.headers: 'InstallType' = [] + self.man: 'InstallType' = [] + self.data: 'InstallType' = [] + self.po_package_name: str = '' self.po = [] - self.install_scripts = [] - self.install_subdirs = [] + self.install_scripts: T.List[build.RunScript] = [] + self.install_subdirs: 'InstallSubdirsType' = [] self.mesonintrospect = mesonintrospect self.version = version class TargetInstallData: - def __init__(self, fname, outdir, aliases, strip, install_name_mappings, rpath_dirs_to_remove, install_rpath, install_mode, optional=False): + def __init__(self, fname: str, outdir: str, aliases: T.Dict[str, str], strip: bool, + install_name_mappings: T.Dict, rpath_dirs_to_remove: T.Set[bytes], + install_rpath: str, install_mode: 'FileMode', optional: bool = False): self.fname = fname self.outdir = outdir self.aliases = aliases @@ -545,14 +553,14 @@ class Backend: paths.append(libdir) return paths - def determine_rpath_dirs(self, target): + def determine_rpath_dirs(self, target: build.BuildTarget) -> T.Tuple[str, ...]: if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror': - result = target.get_link_dep_subdirs() + result: OrderedSet[str] = target.get_link_dep_subdirs() else: result = OrderedSet() result.add('meson-out') result.update(self.rpaths_for_bundled_shared_libraries(target)) - target.rpath_dirs_to_remove.update([d.encode('utf8') for d in result]) + target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result]) return tuple(result) @staticmethod @@ -908,7 +916,7 @@ class Backend: abs_path = self.get_target_filename_abs(a) return os.path.relpath(abs_path, workdir) - def generate_depmf_install(self, d): + def generate_depmf_install(self, d: InstallData) -> None: if self.build.dep_manifest_name is None: return ifilename = os.path.join(self.environment.get_build_dir(), 'depmf.json') @@ -917,7 +925,7 @@ class Backend: with open(ifilename, 'w') as f: f.write(json.dumps(mfobj)) # Copy file from, to, and with mode unchanged - d.data.append([ifilename, ofilename, None]) + d.data.append((ifilename, ofilename, None)) def get_regen_filelist(self): '''List of all files whose alteration means that the build @@ -1205,7 +1213,7 @@ class Backend: with open(install_data_file, 'wb') as ofile: pickle.dump(self.create_install_data(), ofile) - def generate_target_install(self, d): + def generate_target_install(self, d: InstallData) -> None: for t in self.build.get_targets().values(): if not t.should_install(): continue @@ -1234,6 +1242,7 @@ class Backend: # TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more # fine-grained stripping of static archives. should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target(OptionKey('strip'), t) + assert isinstance(should_strip, bool), 'for mypy' # Install primary build output (library/executable/jar, etc) # Done separately because of strip/aliases/rpath if outdirs[0] is not False: @@ -1301,8 +1310,8 @@ class Backend: optional=not t.build_by_default) d.targets.append(i) - def generate_custom_install_script(self, d): - result = [] + def generate_custom_install_script(self, d: InstallData) -> None: + result: T.List[build.RunScript] = [] srcdir = self.environment.get_source_dir() builddir = self.environment.get_build_dir() for i in self.build.install_scripts: @@ -1316,7 +1325,7 @@ class Backend: result.append(build.RunScript(exe, fixed_args)) d.install_scripts = result - def generate_header_install(self, d): + def generate_header_install(self, d: InstallData) -> None: incroot = self.environment.get_includedir() headers = self.build.get_headers() @@ -1331,10 +1340,10 @@ class Backend: msg = 'Invalid header type {!r} can\'t be installed' raise MesonException(msg.format(f)) abspath = f.absolute_path(srcdir, builddir) - i = [abspath, outdir, h.get_custom_install_mode()] + i = (abspath, outdir, h.get_custom_install_mode()) d.headers.append(i) - def generate_man_install(self, d): + def generate_man_install(self, d: InstallData) -> None: manroot = self.environment.get_mandir() man = self.build.get_man() for m in man: @@ -1345,10 +1354,10 @@ class Backend: 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)) - i = [srcabs, dstabs, m.get_custom_install_mode()] + i = (srcabs, dstabs, m.get_custom_install_mode()) d.man.append(i) - def generate_data_install(self, d): + def generate_data_install(self, d: InstallData): data = self.build.get_data() srcdir = self.environment.get_source_dir() builddir = self.environment.get_build_dir() @@ -1360,10 +1369,10 @@ class Backend: for src_file, dst_name in zip(de.sources, de.rename): assert(isinstance(src_file, mesonlib.File)) dst_abs = os.path.join(subdir, dst_name) - i = [src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode] + i = (src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode) d.data.append(i) - def generate_subdir_install(self, d): + def generate_subdir_install(self, d: InstallData) -> None: for sd in self.build.get_install_subdirs(): if sd.from_source_dir: from_dir = self.environment.get_source_dir() @@ -1376,8 +1385,8 @@ class Backend: sd.install_dir) if not sd.strip_directory: dst_dir = os.path.join(dst_dir, os.path.basename(src_dir)) - d.install_subdirs.append([src_dir, dst_dir, sd.install_mode, - sd.exclude]) + d.install_subdirs.append( + (src_dir, dst_dir, sd.install_mode, sd.exclude)) def get_introspection_data(self, target_id: str, target: build.Target) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]: ''' -- cgit v1.1 From efe4547d80bfdd1c17e4e9275bca796f4181e951 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 13 Jan 2021 13:31:34 -0800 Subject: minstall: Add type annotations This adds annotations and fixes a couple of issues (passing Set[bytes] where List[byte] is expected), however, there's some very gross addition of attributes to types going on that I haven't fixed yet, and mypy is very grump about. --- mesonbuild/minstall.py | 95 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 0adcda2..a0120d3 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -22,6 +22,7 @@ import shlex import shutil import subprocess import sys +import typing as T from . import environment from .backend.backends import InstallData @@ -36,13 +37,30 @@ except ImportError: # This is only used for pkexec which is not, so this is fine. main_file = None +if T.TYPE_CHECKING: + from .mesonlib import FileMode + + try: + from typing import Protocol + except AttributeError: + from typing_extensions import Protocol # type: ignore + + class ArgumentType(Protocol): + """Typing information for the object returned by argparse.""" + no_rebuild: bool + only_changed: bool + profile: bool + quiet: bool + wd: str + + symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file, but this will be changed in a future version of Meson to copy the symlink as is. Please update your build definitions so that it will not break when the change happens.''' -selinux_updates = [] +selinux_updates: T.List[str] = [] -def add_arguments(parser): +def add_arguments(parser: argparse.Namespace) -> None: parser.add_argument('-C', default='.', dest='wd', help='directory to cd into before running') parser.add_argument('--profile-self', action='store_true', dest='profile', @@ -55,11 +73,11 @@ def add_arguments(parser): help='Do not print every file that was installed.') class DirMaker: - def __init__(self, lf): + def __init__(self, lf: T.TextIO): self.lf = lf - self.dirs = [] + self.dirs: T.List[str] = [] - def makedirs(self, path, exist_ok=False): + def makedirs(self, path: str, exist_ok: bool = False) -> None: dirname = os.path.normpath(path) dirs = [] while dirname != os.path.dirname(dirname): @@ -76,25 +94,29 @@ class DirMaker: dirs.reverse() self.dirs += dirs - def __enter__(self): + def __enter__(self) -> 'DirMaker': return self - def __exit__(self, exception_type, value, traceback): + def __exit__(self, exception_type: T.Type[Exception], value: T.Any, traceback: T.Any) -> None: self.dirs.reverse() for d in self.dirs: append_to_log(self.lf, d) -def is_executable(path, follow_symlinks=False): + +def is_executable(path: str, follow_symlinks: bool = False) -> bool: '''Checks whether any of the "x" bits are set in the source file mode.''' return bool(os.stat(path, follow_symlinks=follow_symlinks).st_mode & 0o111) -def append_to_log(lf, line): + +def append_to_log(lf: T.TextIO, line: str) -> None: lf.write(line) if not line.endswith('\n'): lf.write('\n') lf.flush() -def set_chown(path, user=None, group=None, dir_fd=None, follow_symlinks=True): + +def set_chown(path: str, user: T.Optional[str] = None, group: T.Optional[str] = None, + dir_fd: T.Optional[int] = None, follow_symlinks: bool = True) -> None: # shutil.chown will call os.chown without passing all the parameters # and particularly follow_symlinks, thus we replace it temporary # with a lambda with all the parameters so that follow_symlinks will @@ -107,21 +129,25 @@ def set_chown(path, user=None, group=None, dir_fd=None, follow_symlinks=True): dir_fd=dir_fd, follow_symlinks=follow_symlinks) shutil.chown(path, user, group) - except Exception: - raise finally: os.chown = real_os_chown -def set_chmod(path, mode, dir_fd=None, follow_symlinks=True): + +def set_chmod(path: str, mode: int, dir_fd: T.Optional[int] = None, + follow_symlinks: bool = True) -> None: try: os.chmod(path, mode, dir_fd=dir_fd, follow_symlinks=follow_symlinks) except (NotImplementedError, OSError, SystemError): if not os.path.islink(path): os.chmod(path, mode, dir_fd=dir_fd) -def sanitize_permissions(path, umask): + +def sanitize_permissions(path: str, umask: T.Union[str, int]) -> None: + # TODO: with python 3.8 or typing_extensions we could replace this with + # `umask: T.Union[T.Literal['preserve'], int]`, which would be mroe correct if umask == 'preserve': return + assert isinstance(umask, int), 'umask should only be "preserver" or an integer' new_perms = 0o777 if is_executable(path, follow_symlinks=False) else 0o666 new_perms &= ~umask try: @@ -130,7 +156,8 @@ def sanitize_permissions(path, umask): msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...' print(msg.format(path, new_perms, e.strerror)) -def set_mode(path, mode, default_umask): + +def set_mode(path: str, mode: T.Optional['FileMode'], default_umask: T.Union[str, int]) -> None: 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) @@ -163,7 +190,8 @@ def set_mode(path, mode, default_umask): else: sanitize_permissions(path, default_umask) -def restore_selinux_contexts(): + +def restore_selinux_contexts() -> None: ''' Restores the SELinux context for files in @selinux_updates @@ -193,7 +221,7 @@ def restore_selinux_contexts(): 'Standard error:', err.decode(), sep='\n') -def get_destdir_path(d, path): +def get_destdir_path(d: InstallData, path: str) -> str: if os.path.isabs(path): output = destdir_join(d.destdir, path) else: @@ -201,7 +229,7 @@ def get_destdir_path(d, path): return output -def check_for_stampfile(fname): +def check_for_stampfile(fname: str) -> str: '''Some languages e.g. Rust have output files whose names are not known at configure time. Check if this is the case and return the real @@ -226,19 +254,20 @@ def check_for_stampfile(fname): return files[0] return fname + class Installer: - def __init__(self, options, lf): + def __init__(self, options: 'ArgumentType', lf: T.TextIO): self.did_install_something = False self.options = options self.lf = lf self.preserved_file_count = 0 - def log(self, msg): + def log(self, msg: str) -> None: if not self.options.quiet: print(msg) - def should_preserve_existing_file(self, from_file, to_file): + def should_preserve_existing_file(self, from_file: str, to_file: str) -> bool: if not self.options.only_changed: return False # Always replace danging symlinks @@ -248,7 +277,8 @@ class Installer: to_time = os.stat(to_file).st_mtime return from_time <= to_time - def do_copyfile(self, from_file, to_file, makedirs=None): + def do_copyfile(self, from_file: str, to_file: str, + makedirs: T.Optional[T.Tuple[T.Any, str]] = None) -> bool: outdir = os.path.split(to_file)[0] if not os.path.isfile(from_file) and not os.path.islink(from_file): raise RuntimeError('Tried to install something that isn\'t a file:' @@ -286,7 +316,9 @@ class Installer: append_to_log(self.lf, to_file) return True - def do_copydir(self, data, src_dir, dst_dir, exclude, install_mode): + def do_copydir(self, data: InstallData, src_dir: str, dst_dir: str, + exclude: T.Optional[T.Tuple[T.Set[str], T.Set[str]]], + install_mode: 'FileMode') -> None: ''' Copies the contents of directory @src_dir into @dst_dir. @@ -360,7 +392,7 @@ class Installer: raise MesonVersionMismatchException(obj.version, coredata_version) return obj - def do_install(self, datafilename): + def do_install(self, datafilename: str) -> None: with open(datafilename, 'rb') as ifile: d = self.check_installdata(pickle.load(ifile)) @@ -368,6 +400,7 @@ class Installer: d.fullprefix = destdir_join(d.destdir, d.prefix) if d.install_umask != 'preserve': + assert isinstance(d.install_umask, int), 'for mypy' os.umask(d.install_umask) self.did_install_something = False @@ -395,7 +428,7 @@ class Installer: else: raise - def install_subdirs(self, d): + def install_subdirs(self, d: InstallData) -> None: for (src_dir, dst_dir, mode, exclude) in d.install_subdirs: self.did_install_something = True full_dst_dir = get_destdir_path(d, dst_dir) @@ -403,7 +436,7 @@ class Installer: d.dirmaker.makedirs(full_dst_dir, exist_ok=True) self.do_copydir(d, src_dir, full_dst_dir, exclude, mode) - def install_data(self, d): + def install_data(self, d: InstallData) -> None: for i in d.data: fullfilename = i[0] outfilename = get_destdir_path(d, i[1]) @@ -413,7 +446,7 @@ class Installer: self.did_install_something = True set_mode(outfilename, mode, d.install_umask) - def install_man(self, d): + def install_man(self, d: InstallData) -> None: for m in d.man: full_source_filename = m[0] outfilename = get_destdir_path(d, m[1]) @@ -423,7 +456,7 @@ class Installer: self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def install_headers(self, d): + def install_headers(self, d: InstallData) -> None: for t in d.headers: fullfilename = t[0] fname = os.path.basename(fullfilename) @@ -434,7 +467,7 @@ class Installer: self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def run_install_script(self, d): + def run_install_script(self, d: InstallData) -> None: env = {'MESON_SOURCE_ROOT': d.source_dir, 'MESON_BUILD_ROOT': d.build_dir, 'MESON_INSTALL_PREFIX': d.prefix, @@ -463,7 +496,7 @@ class Installer: print('FAILED: install script \'{}\' exit code {}, stopped'.format(name, rc)) sys.exit(rc) - def install_targets(self, d): + def install_targets(self, d: InstallData) -> None: for t in d.targets: if not os.path.exists(t.fname): # For example, import libraries of shared modules are optional @@ -557,7 +590,7 @@ def rebuild_all(wd: str) -> bool: return True -def run(opts): +def run(opts: 'ArgumentType') -> int: datafilename = 'meson-private/install.dat' private_dir = os.path.dirname(datafilename) log_dir = os.path.join(private_dir, '../meson-logs') -- cgit v1.1 From 2a30f0bd35461b54672c5021cc84e6c8a5372253 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 11:48:29 -0800 Subject: minstall: Pass DirMaker separately instead of adding to instance Add a new attribute to an object outside of the initializer of construtor is considered an antipattern for good reason, it's gross, it's confusing, and it often leads to AttributeErrors down some paths. Let's not do that. --- mesonbuild/minstall.py | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index a0120d3..4ab195b 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -318,7 +318,7 @@ class Installer: def do_copydir(self, data: InstallData, src_dir: str, dst_dir: str, exclude: T.Optional[T.Tuple[T.Set[str], T.Set[str]]], - install_mode: 'FileMode') -> None: + install_mode: 'FileMode', dm: DirMaker) -> None: ''' Copies the contents of directory @src_dir into @dst_dir. @@ -364,7 +364,7 @@ class Installer: if os.path.exists(abs_dst): print('Tried to copy directory {} but a file of that name already exists.'.format(abs_dst)) sys.exit(1) - data.dirmaker.makedirs(abs_dst) + dm.makedirs(abs_dst) shutil.copystat(abs_src, abs_dst) sanitize_permissions(abs_dst, data.install_umask) for f in files: @@ -405,15 +405,14 @@ class Installer: self.did_install_something = False try: - d.dirmaker = DirMaker(self.lf) - with d.dirmaker: - self.install_subdirs(d) # Must be first, because it needs to delete the old subtree. - self.install_targets(d) - self.install_headers(d) - self.install_man(d) - self.install_data(d) + with DirMaker(self.lf) as dm: + self.install_subdirs(d, dm) # Must be first, because it needs to delete the old subtree. + self.install_targets(d, dm) + self.install_headers(d, dm) + self.install_man(d, dm) + self.install_data(d, dm) restore_selinux_contexts() - self.run_install_script(d) + self.run_install_script(d, dm) if not self.did_install_something: self.log('Nothing to install.') if not self.options.quiet and self.preserved_file_count > 0: @@ -428,46 +427,46 @@ class Installer: else: raise - def install_subdirs(self, d: InstallData) -> None: + def install_subdirs(self, d: InstallData, dm: DirMaker) -> None: for (src_dir, dst_dir, mode, exclude) in d.install_subdirs: self.did_install_something = True full_dst_dir = get_destdir_path(d, dst_dir) self.log('Installing subdir {} to {}'.format(src_dir, full_dst_dir)) - d.dirmaker.makedirs(full_dst_dir, exist_ok=True) - self.do_copydir(d, src_dir, full_dst_dir, exclude, mode) + dm.makedirs(full_dst_dir, exist_ok=True) + self.do_copydir(d, src_dir, full_dst_dir, exclude, mode, dm) - def install_data(self, d: InstallData) -> None: + def install_data(self, d: InstallData, dm: DirMaker) -> None: for i in d.data: fullfilename = i[0] outfilename = get_destdir_path(d, i[1]) mode = i[2] outdir = os.path.dirname(outfilename) - if self.do_copyfile(fullfilename, outfilename, makedirs=(d.dirmaker, outdir)): + if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, mode, d.install_umask) - def install_man(self, d: InstallData) -> None: + def install_man(self, d: InstallData, dm: DirMaker) -> None: for m in d.man: full_source_filename = m[0] outfilename = get_destdir_path(d, m[1]) outdir = os.path.dirname(outfilename) install_mode = m[2] - if self.do_copyfile(full_source_filename, outfilename, makedirs=(d.dirmaker, outdir)): + if self.do_copyfile(full_source_filename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def install_headers(self, d: InstallData) -> None: + def install_headers(self, d: InstallData, dm: DirMaker) -> None: for t in d.headers: fullfilename = t[0] fname = os.path.basename(fullfilename) outdir = get_destdir_path(d, t[1]) outfilename = os.path.join(outdir, fname) install_mode = t[2] - if self.do_copyfile(fullfilename, outfilename, makedirs=(d.dirmaker, outdir)): + if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def run_install_script(self, d: InstallData) -> None: + def run_install_script(self, d: InstallData, dm: DirMaker) -> None: env = {'MESON_SOURCE_ROOT': d.source_dir, 'MESON_BUILD_ROOT': d.build_dir, 'MESON_INSTALL_PREFIX': d.prefix, @@ -496,7 +495,7 @@ class Installer: print('FAILED: install script \'{}\' exit code {}, stopped'.format(name, rc)) sys.exit(rc) - def install_targets(self, d: InstallData) -> None: + def install_targets(self, d: InstallData, dm: DirMaker) -> None: for t in d.targets: if not os.path.exists(t.fname): # For example, import libraries of shared modules are optional @@ -518,7 +517,7 @@ class Installer: if not os.path.exists(fname): raise RuntimeError('File {!r} could not be found'.format(fname)) elif os.path.isfile(fname): - file_copied = self.do_copyfile(fname, outname, makedirs=(d.dirmaker, outdir)) + file_copied = self.do_copyfile(fname, outname, makedirs=(dm, outdir)) set_mode(outname, install_mode, d.install_umask) if should_strip and d.strip_bin is not None: if fname.endswith('.jar'): @@ -541,8 +540,8 @@ class Installer: elif os.path.isdir(fname): fname = os.path.join(d.build_dir, fname.rstrip('/')) outname = os.path.join(outdir, os.path.basename(fname)) - d.dirmaker.makedirs(outdir, exist_ok=True) - self.do_copydir(d, fname, outname, None, install_mode) + dm.makedirs(outdir, exist_ok=True) + self.do_copydir(d, fname, outname, None, install_mode, dm) else: raise RuntimeError('Unknown file type for {!r}'.format(fname)) printed_symlink_error = False -- cgit v1.1 From c49ad7ca4877aabaebdb75c16814c9e6653c697e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 11:54:09 -0800 Subject: minstall: Pass destdir and fullprefix instead of adding them to instance Same idea as the last patch, just different data --- mesonbuild/minstall.py | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 4ab195b..5389003 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -221,11 +221,11 @@ def restore_selinux_contexts() -> None: 'Standard error:', err.decode(), sep='\n') -def get_destdir_path(d: InstallData, path: str) -> str: +def get_destdir_path(destdir: str, fullprefix: str, path: str) -> str: if os.path.isabs(path): - output = destdir_join(d.destdir, path) + output = destdir_join(destdir, path) else: - output = os.path.join(d.fullprefix, path) + output = os.path.join(fullprefix, path) return output @@ -396,30 +396,30 @@ class Installer: with open(datafilename, 'rb') as ifile: d = self.check_installdata(pickle.load(ifile)) - d.destdir = os.environ.get('DESTDIR', '') - d.fullprefix = destdir_join(d.destdir, d.prefix) + destdir = os.environ.get('DESTDIR', '') + fullprefix = destdir_join(destdir, d.prefix) if d.install_umask != 'preserve': - assert isinstance(d.install_umask, int), 'for mypy' + assert isinstance(d.install_umask, int) os.umask(d.install_umask) self.did_install_something = False try: with DirMaker(self.lf) as dm: - self.install_subdirs(d, dm) # Must be first, because it needs to delete the old subtree. - self.install_targets(d, dm) - self.install_headers(d, dm) - self.install_man(d, dm) - self.install_data(d, dm) + self.install_subdirs(d, dm, destdir, fullprefix) # Must be first, because it needs to delete the old subtree. + self.install_targets(d, dm, destdir, fullprefix) + self.install_headers(d, dm, destdir, fullprefix) + self.install_man(d, dm, destdir, fullprefix) + self.install_data(d, dm, destdir, fullprefix) restore_selinux_contexts() - self.run_install_script(d, dm) + self.run_install_script(d, fullprefix) if not self.did_install_something: self.log('Nothing to install.') if not self.options.quiet and self.preserved_file_count > 0: self.log('Preserved {} unchanged files, see {} for the full list' .format(self.preserved_file_count, os.path.normpath(self.lf.name))) except PermissionError: - if shutil.which('pkexec') is not None and 'PKEXEC_UID' not in os.environ and d.destdir is None: + if shutil.which('pkexec') is not None and 'PKEXEC_UID' not in os.environ and destdir is None: print('Installation failed due to insufficient permissions.') print('Attempting to use polkit to gain elevated privileges...') os.execlp('pkexec', 'pkexec', sys.executable, main_file, *sys.argv[1:], @@ -427,50 +427,50 @@ class Installer: else: raise - def install_subdirs(self, d: InstallData, dm: DirMaker) -> None: + def install_subdirs(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for (src_dir, dst_dir, mode, exclude) in d.install_subdirs: self.did_install_something = True - full_dst_dir = get_destdir_path(d, dst_dir) + full_dst_dir = get_destdir_path(destdir, fullprefix, dst_dir) self.log('Installing subdir {} to {}'.format(src_dir, full_dst_dir)) dm.makedirs(full_dst_dir, exist_ok=True) self.do_copydir(d, src_dir, full_dst_dir, exclude, mode, dm) - def install_data(self, d: InstallData, dm: DirMaker) -> None: + def install_data(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for i in d.data: fullfilename = i[0] - outfilename = get_destdir_path(d, i[1]) + outfilename = get_destdir_path(destdir, fullprefix, i[1]) mode = i[2] outdir = os.path.dirname(outfilename) if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, mode, d.install_umask) - def install_man(self, d: InstallData, dm: DirMaker) -> None: + def install_man(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for m in d.man: full_source_filename = m[0] - outfilename = get_destdir_path(d, m[1]) + outfilename = get_destdir_path(destdir, fullprefix, m[1]) outdir = os.path.dirname(outfilename) install_mode = m[2] if self.do_copyfile(full_source_filename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def install_headers(self, d: InstallData, dm: DirMaker) -> None: + def install_headers(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for t in d.headers: fullfilename = t[0] fname = os.path.basename(fullfilename) - outdir = get_destdir_path(d, t[1]) + outdir = get_destdir_path(destdir, fullprefix, t[1]) outfilename = os.path.join(outdir, fname) install_mode = t[2] if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)): self.did_install_something = True set_mode(outfilename, install_mode, d.install_umask) - def run_install_script(self, d: InstallData, dm: DirMaker) -> None: + def run_install_script(self, d: InstallData, fullprefix: str) -> None: env = {'MESON_SOURCE_ROOT': d.source_dir, 'MESON_BUILD_ROOT': d.build_dir, 'MESON_INSTALL_PREFIX': d.prefix, - 'MESON_INSTALL_DESTDIR_PREFIX': d.fullprefix, + 'MESON_INSTALL_DESTDIR_PREFIX': fullprefix, 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in d.mesonintrospect]), } if self.options.quiet: @@ -495,7 +495,7 @@ class Installer: print('FAILED: install script \'{}\' exit code {}, stopped'.format(name, rc)) sys.exit(rc) - def install_targets(self, d: InstallData, dm: DirMaker) -> None: + def install_targets(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for t in d.targets: if not os.path.exists(t.fname): # For example, import libraries of shared modules are optional @@ -506,7 +506,7 @@ class Installer: raise RuntimeError('File {!r} could not be found'.format(t.fname)) file_copied = False # not set when a directory is copied fname = check_for_stampfile(t.fname) - outdir = get_destdir_path(d, t.outdir) + outdir = get_destdir_path(destdir, fullprefix, t.outdir) outname = os.path.join(outdir, os.path.basename(fname)) final_path = os.path.join(d.prefix, t.outdir, os.path.basename(fname)) aliases = t.aliases -- cgit v1.1 From caab4d3d92e90430a77e7694a1582461b0fe2913 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 12:00:22 -0800 Subject: minstall: Fix signature of monkeypatched os.chown this also clears up the last of the mypy problems in minstall, yay! --- mesonbuild/minstall.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 5389003..3e425eb 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -124,10 +124,19 @@ def set_chown(path: str, user: T.Optional[str] = None, group: T.Optional[str] = # Not nice, but better than actually rewriting shutil.chown until # this python bug is fixed: https://bugs.python.org/issue18108 real_os_chown = os.chown + + def chown(path: T.Union[int, str, 'os.PathLike[str]', bytes, 'os.PathLike[bytes]'], + uid: int, gid: int, *, dir_fd: T.Optional[int] = dir_fd, + follow_symlinks: bool = follow_symlinks) -> None: + """Override the default behavior of os.chown + + Use a real function rather than a lambda to help mypy out. Also real + functions are faster. + """ + real_os_chown(path, gid, uid, dir_fd=dir_fd, follow_symlinks=follow_symlinks) + try: - os.chown = lambda p, u, g: real_os_chown(p, u, g, - dir_fd=dir_fd, - follow_symlinks=follow_symlinks) + os.chown = chown shutil.chown(path, user, group) finally: os.chown = real_os_chown -- cgit v1.1 From 1849a9e9b7218ecb695a8414eba9e15ebdc3fe1e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 12 Jan 2021 12:49:58 -0800 Subject: run_mypy: add minstall --- run_mypy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/run_mypy.py b/run_mypy.py index 888403c..daf7431 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -26,6 +26,7 @@ modules = [ 'mesonbuild/mcompile.py', 'mesonbuild/mesonlib.py', 'mesonbuild/minit.py', + 'mesonbuild/minstall.py', 'mesonbuild/mintro.py', 'mesonbuild/mlog.py', 'mesonbuild/modules/fs.py', -- cgit v1.1