From 311a07c39a34e2aa4c193b4188d47f5e50ca1eda Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 13 Apr 2020 10:17:49 -0400 Subject: interpreter: Rename dirname to subp_name dirname is confusing because the name of a subproject does not always match its directory name, the wrap file can define another directory. For example foo.wrap will often extract the subproject into foo-1.2 directory, in that case the subproject name is 'foo' and the subproject directory is 'foo-1.2'. --- mesonbuild/interpreter.py | 130 +++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 65 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index f25a6f3..713372d 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2840,55 +2840,55 @@ external dependencies (including libraries) must go to "dependencies".''') def func_subproject(self, nodes, args, kwargs): if len(args) != 1: raise InterpreterException('Subproject takes exactly one argument') - dirname = args[0] - return self.do_subproject(dirname, 'meson', kwargs) + subp_name = args[0] + return self.do_subproject(subp_name, 'meson', kwargs) - def disabled_subproject(self, dirname, disabled_feature=None, exception=None): - sub = SubprojectHolder(None, self.subproject_dir, dirname, + def disabled_subproject(self, subp_name, disabled_feature=None, exception=None): + sub = SubprojectHolder(None, self.subproject_dir, subp_name, disabled_feature=disabled_feature, exception=exception) - self.subprojects[dirname] = sub + self.subprojects[subp_name] = sub return sub - def get_subproject(self, dirname): - sub = self.subprojects.get(dirname) + def get_subproject(self, subp_name): + sub = self.subprojects.get(subp_name) if sub and sub.found(): return sub return None - def do_subproject(self, dirname: str, method: str, kwargs): + def do_subproject(self, subp_name: str, method: str, kwargs): disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: - mlog.log('Subproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled') - return self.disabled_subproject(dirname, disabled_feature=feature) + mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled') + return self.disabled_subproject(subp_name, disabled_feature=feature) default_options = mesonlib.stringlistify(kwargs.get('default_options', [])) default_options = coredata.create_options_dict(default_options) - if dirname == '': - raise InterpreterException('Subproject dir name must not be empty.') - if dirname[0] == '.': - raise InterpreterException('Subproject dir name must not start with a period.') - if '..' in dirname: + if subp_name == '': + raise InterpreterException('Subproject name must not be empty.') + if subp_name[0] == '.': + raise InterpreterException('Subproject name must not start with a period.') + if '..' in subp_name: raise InterpreterException('Subproject name must not contain a ".." path segment.') - if os.path.isabs(dirname): + if os.path.isabs(subp_name): raise InterpreterException('Subproject name must not be an absolute path.') - if has_path_sep(dirname): + if has_path_sep(subp_name): mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.', location=self.current_node) - if dirname in self.subproject_stack: - fullstack = self.subproject_stack + [dirname] + if subp_name in self.subproject_stack: + fullstack = self.subproject_stack + [subp_name] incpath = ' => '.join(fullstack) raise InvalidCode('Recursive include of subprojects: %s.' % incpath) - if dirname in self.subprojects: - subproject = self.subprojects[dirname] + if subp_name in self.subprojects: + subproject = self.subprojects[subp_name] if required and not subproject.found(): raise InterpreterException('Subproject "%s/%s" required but not found.' % ( - self.subproject_dir, dirname)) + self.subproject_dir, subp_name)) return subproject r = self.environment.wrap_resolver try: - resolved = r.resolve(dirname, method, self.subproject) + resolved = r.resolve(subp_name, method, self.subproject) except wrap.WrapException as e: subprojdir = os.path.join(self.subproject_dir, r.directory) if isinstance(e, wrap.WrapNotFoundException): @@ -2896,11 +2896,11 @@ external dependencies (including libraries) must go to "dependencies".''') # the directory doesn't exist, try to give some helpful # advice if it's a nested subproject that needs # promotion... - self.print_nested_info(dirname) + self.print_nested_info(subp_name) if not required: mlog.log(e) mlog.log('Subproject ', mlog.bold(subprojdir), 'is buildable:', mlog.red('NO'), '(disabling)') - return self.disabled_subproject(dirname, exception=e) + return self.disabled_subproject(subp_name, exception=e) raise e subdir = os.path.join(self.subproject_dir, resolved) @@ -2910,14 +2910,14 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.log() with mlog.nested(): - mlog.log('Executing subproject', mlog.bold(dirname), 'method', mlog.bold(method), '\n') + mlog.log('Executing subproject', mlog.bold(subp_name), 'method', mlog.bold(method), '\n') try: if method == 'meson': - return self._do_subproject_meson(dirname, subdir, default_options, kwargs) + return self._do_subproject_meson(subp_name, subdir, default_options, kwargs) elif method == 'cmake': - return self._do_subproject_cmake(dirname, subdir, subdir_abs, default_options, kwargs) + return self._do_subproject_cmake(subp_name, subdir, subdir_abs, default_options, kwargs) else: - raise InterpreterException('The method {} is invalid for the subproject {}'.format(method, dirname)) + raise InterpreterException('The method {} is invalid for the subproject {}'.format(method, subp_name)) # Invalid code is always an error except InvalidCode: raise @@ -2927,18 +2927,18 @@ external dependencies (including libraries) must go to "dependencies".''') # Suppress the 'ERROR:' prefix because this exception is not # fatal and VS CI treat any logs with "ERROR:" as fatal. mlog.exception(e, prefix=mlog.yellow('Exception:')) - mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)') - return self.disabled_subproject(dirname, exception=e) + mlog.log('\nSubproject', mlog.bold(subp_name), 'is buildable:', mlog.red('NO'), '(disabling)') + return self.disabled_subproject(subp_name, exception=e) raise e - def _do_subproject_meson(self, dirname, subdir, default_options, kwargs, ast=None, build_def_files=None): + def _do_subproject_meson(self, subp_name, subdir, default_options, kwargs, ast=None, build_def_files=None): with mlog.nested(): new_build = self.build.copy() - subi = Interpreter(new_build, self.backend, dirname, subdir, self.subproject_dir, + subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, self.modules, default_options, ast=ast) subi.subprojects = self.subprojects - subi.subproject_stack = self.subproject_stack + [dirname] + subi.subproject_stack = self.subproject_stack + [subp_name] current_active = self.active_projectname current_warnings_counter = mlog.log_warnings_counter mlog.log_warnings_counter = 0 @@ -2946,7 +2946,7 @@ external dependencies (including libraries) must go to "dependencies".''') subi_warnings = mlog.log_warnings_counter mlog.log_warnings_counter = current_warnings_counter - mlog.log('Subproject', mlog.bold(dirname), 'finished.') + mlog.log('Subproject', mlog.bold(subp_name), 'finished.') mlog.log() @@ -2954,10 +2954,10 @@ external dependencies (including libraries) must go to "dependencies".''') pv = subi.project_version wanted = kwargs['version'] if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]: - raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted)) + raise InterpreterException('Subproject %s version is %s but %s required.' % (subp_name, pv, wanted)) self.active_projectname = current_active self.subprojects.update(subi.subprojects) - self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname, + self.subprojects[subp_name] = SubprojectHolder(subi, self.subproject_dir, subp_name, warnings=subi_warnings) # Duplicates are possible when subproject uses files from project root if build_def_files: @@ -2965,11 +2965,11 @@ external dependencies (including libraries) must go to "dependencies".''') else: self.build_def_files = list(set(self.build_def_files + subi.build_def_files)) self.build.merge(subi.build) - self.build.subprojects[dirname] = subi.project_version + self.build.subprojects[subp_name] = subi.project_version self.summary.update(subi.summary) - return self.subprojects[dirname] + return self.subprojects[subp_name] - def _do_subproject_cmake(self, dirname, subdir, subdir_abs, default_options, kwargs): + def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, kwargs): with mlog.nested(): new_build = self.build.copy() prefix = self.coredata.builtins['prefix'].value @@ -3007,7 +3007,7 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.cmd_ci_include(meson_filename) mlog.log() - result = self._do_subproject_meson(dirname, subdir, default_options, kwargs, ast, cm_int.bs_files) + result = self._do_subproject_meson(subp_name, subdir, default_options, kwargs, ast, cm_int.bs_files) result.cm_interpreter = cm_int mlog.log() @@ -3582,8 +3582,8 @@ external dependencies (including libraries) must go to "dependencies".''') def notfound_dependency(self): return DependencyHolder(NotFoundDependency(self.environment), self.subproject) - def verify_fallback_consistency(self, dirname, varname, cached_dep): - subi = self.get_subproject(dirname) + def verify_fallback_consistency(self, subp_name, varname, cached_dep): + subi = self.get_subproject(subp_name) if not cached_dep or not varname or not subi or not cached_dep.found(): return dep = subi.get_variable_method([varname], {}) @@ -3591,13 +3591,13 @@ external dependencies (including libraries) must go to "dependencies".''') m = 'Inconsistency: Subproject has overridden the dependency with another variable than {!r}' raise DependencyException(m.format(varname)) - def get_subproject_dep(self, name, display_name, dirname, varname, kwargs): + def get_subproject_dep(self, name, display_name, subp_name, varname, kwargs): required = kwargs.get('required', True) wanted = mesonlib.stringlistify(kwargs.get('version', [])) - subproj_path = os.path.join(self.subproject_dir, dirname) + subproj_path = os.path.join(self.subproject_dir, subp_name) dep = self.notfound_dependency() try: - subproject = self.subprojects[dirname] + subproject = self.subprojects[subp_name] _, cached_dep = self._find_cached_dep(name, display_name, kwargs) if varname is None: # Assuming the subproject overridden the dependency we want @@ -3614,19 +3614,19 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.bold(subproj_path), 'found:', mlog.red('NO')) return self.notfound_dependency() if subproject.found(): - self.verify_fallback_consistency(dirname, varname, cached_dep) - dep = self.subprojects[dirname].get_variable_method([varname], {}) + self.verify_fallback_consistency(subp_name, varname, cached_dep) + dep = self.subprojects[subp_name].get_variable_method([varname], {}) except InvalidArguments: pass if not isinstance(dep, DependencyHolder): raise InvalidCode('Fetched variable {!r} in the subproject {!r} is ' - 'not a dependency object.'.format(varname, dirname)) + 'not a dependency object.'.format(varname, subp_name)) if not dep.found(): if required: raise DependencyException('Could not find dependency {} in subproject {}' - ''.format(varname, dirname)) + ''.format(varname, subp_name)) # If the dependency is not required, don't raise an exception mlog.log('Dependency', mlog.bold(display_name), 'from subproject', mlog.bold(subproj_path), 'found:', mlog.red('NO')) @@ -3637,7 +3637,7 @@ external dependencies (including libraries) must go to "dependencies".''') if required: raise DependencyException('Version {} of subproject dependency {} already ' 'cached, requested incompatible version {} for ' - 'dep {}'.format(found, dirname, wanted, display_name)) + 'dep {}'.format(found, subp_name, wanted, display_name)) mlog.log('Dependency', mlog.bold(display_name), 'from subproject', mlog.bold(subproj_path), 'found:', mlog.red('NO'), @@ -3733,8 +3733,8 @@ external dependencies (including libraries) must go to "dependencies".''') provider = self.environment.wrap_resolver.find_dep_provider(name) if not provider and allow_fallback is True: raise InvalidArguments('Fallback wrap or subproject not found for dependency \'%s\'' % name) - dirname = mesonlib.listify(provider)[0] - if provider and (allow_fallback is True or required or self.get_subproject(dirname)): + subp_name = mesonlib.listify(provider)[0] + if provider and (allow_fallback is True or required or self.get_subproject(subp_name)): fallback = provider if 'default_options' in kwargs and not fallback: @@ -3752,8 +3752,8 @@ external dependencies (including libraries) must go to "dependencies".''') identifier, cached_dep = self._find_cached_dep(name, display_name, kwargs) if cached_dep: if fallback: - dirname, varname = self.get_subproject_infos(fallback) - self.verify_fallback_consistency(dirname, varname, cached_dep) + subp_name, varname = self.get_subproject_infos(fallback) + self.verify_fallback_consistency(subp_name, varname, cached_dep) if required and not cached_dep.found(): m = 'Dependency {!r} was already checked and was not found' raise DependencyException(m.format(display_name)) @@ -3762,16 +3762,16 @@ external dependencies (including libraries) must go to "dependencies".''') if fallback: # If the dependency has already been configured, possibly by # a higher level project, try to use it first. - dirname, varname = self.get_subproject_infos(fallback) - if self.get_subproject(dirname): - return self.get_subproject_dep(name, display_name, dirname, varname, kwargs) + subp_name, varname = self.get_subproject_infos(fallback) + if self.get_subproject(subp_name): + return self.get_subproject_dep(name, display_name, subp_name, varname, kwargs) wrap_mode = self.coredata.get_builtin_option('wrap_mode') force_fallback_for = self.coredata.get_builtin_option('force_fallback_for') force_fallback = (force_fallback or wrap_mode == WrapMode.forcefallback or name in force_fallback_for or - dirname in force_fallback_for) + subp_name in force_fallback_for) if name != '' and (not fallback or not force_fallback): self._handle_featurenew_dependencies(name) @@ -3825,13 +3825,13 @@ external dependencies (including libraries) must go to "dependencies".''') return fbinfo def dependency_fallback(self, name, display_name, fallback, kwargs): - dirname, varname = self.get_subproject_infos(fallback) + subp_name, varname = self.get_subproject_infos(fallback) required = kwargs.get('required', True) # Explicitly listed fallback preferences for specific subprojects # take precedence over wrap-mode force_fallback_for = self.coredata.get_builtin_option('force_fallback_for') - if name in force_fallback_for or dirname in force_fallback_for: + if name in force_fallback_for or subp_name in force_fallback_for: mlog.log('Looking for a fallback subproject for the dependency', mlog.bold(display_name), 'because:\nUse of fallback was forced for that specific subproject') elif self.coredata.get_builtin_option('wrap_mode') == WrapMode.nofallback: @@ -3852,8 +3852,8 @@ external dependencies (including libraries) must go to "dependencies".''') 'default_options': kwargs.get('default_options', []), 'required': required, } - self.do_subproject(dirname, 'meson', sp_kwargs) - return self.get_subproject_dep(name, display_name, dirname, varname, kwargs) + self.do_subproject(subp_name, 'meson', sp_kwargs) + return self.get_subproject_dep(name, display_name, subp_name, varname, kwargs) @FeatureNewKwargs('executable', '0.42.0', ['implib']) @permittedKwargs(permitted_kwargs['executable']) @@ -4709,11 +4709,11 @@ This will probably not work. Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value), location=self.current_node) - def evaluate_subproject_info(self, path_from_source_root, subproject_dirname): + def evaluate_subproject_info(self, path_from_source_root, subproject_dir): depth = 0 subproj_name = '' segs = PurePath(path_from_source_root).parts - segs_spd = PurePath(subproject_dirname).parts + segs_spd = PurePath(subproject_dir).parts while segs and segs[0] == segs_spd[0]: if len(segs_spd) == 1: subproj_name = segs[1] -- cgit v1.1 From 6333ee88c1a243f28b3a7a9bce2dd003b541280a Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 13 Apr 2020 14:35:06 -0400 Subject: Merge wraps from subprojects into wraps from main project wraps from subprojects are now merged into the list of wraps from main project, so they can be used to download dependencies of dependencies instead of having to promote wraps manually. If multiple projects provides the same wrap file, the first one to be configured wins. This also fix usage of sub-subproject that don't have wrap files. We can now configure B when its source tree is at `subprojects/A/subprojects/B/`. This has the implication that we cannot assume that subproject "foo" is at `self.subproject_dir / 'foo'` any more. --- mesonbuild/interpreter.py | 94 +++++++++++++++++----------------------------- mesonbuild/msubprojects.py | 2 +- mesonbuild/wrap/wrap.py | 47 ++++++++++++++++++----- 3 files changed, 72 insertions(+), 71 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 713372d..5104af6 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1010,15 +1010,14 @@ class Test(InterpreterObject): class SubprojectHolder(InterpreterObject, ObjectHolder): - def __init__(self, subinterpreter, subproject_dir, name, warnings=0, disabled_feature=None, + def __init__(self, subinterpreter, subdir, warnings=0, disabled_feature=None, exception=None): InterpreterObject.__init__(self) ObjectHolder.__init__(self, subinterpreter) - self.name = name self.warnings = warnings self.disabled_feature = disabled_feature self.exception = exception - self.subproject_dir = subproject_dir + self.subdir = PurePath(subdir).as_posix() self.methods.update({'get_variable': self.get_variable_method, 'found': self.found_method, }) @@ -1037,8 +1036,7 @@ class SubprojectHolder(InterpreterObject, ObjectHolder): if len(args) < 1 or len(args) > 2: raise InterpreterException('Get_variable takes one or two arguments.') if not self.found(): - raise InterpreterException('Subproject "%s/%s" disabled can\'t get_variable on it.' % ( - self.subproject_dir, self.name)) + raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir)) varname = args[0] if not isinstance(varname, str): raise InterpreterException('Get_variable first argument must be a string.') @@ -2844,7 +2842,7 @@ external dependencies (including libraries) must go to "dependencies".''') return self.do_subproject(subp_name, 'meson', kwargs) def disabled_subproject(self, subp_name, disabled_feature=None, exception=None): - sub = SubprojectHolder(None, self.subproject_dir, subp_name, + sub = SubprojectHolder(None, os.path.join(self.subproject_dir, subp_name), disabled_feature=disabled_feature, exception=exception) self.subprojects[subp_name] = sub return sub @@ -2882,28 +2880,19 @@ external dependencies (including libraries) must go to "dependencies".''') if subp_name in self.subprojects: subproject = self.subprojects[subp_name] if required and not subproject.found(): - raise InterpreterException('Subproject "%s/%s" required but not found.' % ( - self.subproject_dir, subp_name)) + raise InterpreterException('Subproject "%s" required but not found.' % (subproject.subdir)) return subproject r = self.environment.wrap_resolver try: - resolved = r.resolve(subp_name, method, self.subproject) + subdir = r.resolve(subp_name, method, self.subproject) except wrap.WrapException as e: - subprojdir = os.path.join(self.subproject_dir, r.directory) - if isinstance(e, wrap.WrapNotFoundException): - # if the reason subproject execution failed was because - # the directory doesn't exist, try to give some helpful - # advice if it's a nested subproject that needs - # promotion... - self.print_nested_info(subp_name) if not required: mlog.log(e) - mlog.log('Subproject ', mlog.bold(subprojdir), 'is buildable:', mlog.red('NO'), '(disabling)') + mlog.log('Subproject ', mlog.bold(subp_name), 'is buildable:', mlog.red('NO'), '(disabling)') return self.disabled_subproject(subp_name, exception=e) raise e - subdir = os.path.join(self.subproject_dir, resolved) subdir_abs = os.path.join(self.environment.get_source_dir(), subdir) os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) self.global_args_frozen = True @@ -2927,7 +2916,7 @@ external dependencies (including libraries) must go to "dependencies".''') # Suppress the 'ERROR:' prefix because this exception is not # fatal and VS CI treat any logs with "ERROR:" as fatal. mlog.exception(e, prefix=mlog.yellow('Exception:')) - mlog.log('\nSubproject', mlog.bold(subp_name), 'is buildable:', mlog.red('NO'), '(disabling)') + mlog.log('\nSubproject', mlog.bold(subdir), 'is buildable:', mlog.red('NO'), '(disabling)') return self.disabled_subproject(subp_name, exception=e) raise e @@ -2957,8 +2946,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Subproject %s version is %s but %s required.' % (subp_name, pv, wanted)) self.active_projectname = current_active self.subprojects.update(subi.subprojects) - self.subprojects[subp_name] = SubprojectHolder(subi, self.subproject_dir, subp_name, - warnings=subi_warnings) + self.subprojects[subp_name] = SubprojectHolder(subi, subdir, warnings=subi_warnings) # Duplicates are possible when subproject uses files from project root if build_def_files: self.build_def_files = list(set(self.build_def_files + build_def_files)) @@ -3157,8 +3145,11 @@ external dependencies (including libraries) must go to "dependencies".''') 'license': proj_license} if self.subproject in self.build.projects: raise InvalidCode('Second call to project().') - if not self.is_subproject() and 'subproject_dir' in kwargs: - spdirname = kwargs['subproject_dir'] + + # spdirname is the subproject_dir for this project, relative to self.subdir. + # self.subproject_dir is the subproject_dir for the main project, relative to top source dir. + spdirname = kwargs.get('subproject_dir') + if spdirname: if not isinstance(spdirname, str): raise InterpreterException('Subproject_dir must be a string') if os.path.isabs(spdirname): @@ -3167,13 +3158,20 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Subproject_dir must not begin with a period.') if '..' in spdirname: raise InterpreterException('Subproject_dir must not contain a ".." segment.') - self.subproject_dir = spdirname - + if not self.is_subproject(): + self.subproject_dir = spdirname + else: + spdirname = 'subprojects' self.build.subproject_dir = self.subproject_dir - if not self.is_subproject(): - wrap_mode = self.coredata.get_builtin_option('wrap_mode') - subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir) - self.environment.wrap_resolver = wrap.Resolver(subproject_dir_abs, wrap_mode) + + # Load wrap files from this (sub)project. + wrap_mode = self.coredata.get_builtin_option('wrap_mode') + subdir = os.path.join(self.subdir, spdirname) + r = wrap.Resolver(self.environment.get_source_dir(), subdir, wrap_mode) + if self.is_subproject(): + self.environment.wrap_resolver.merge_wraps(r) + else: + self.environment.wrap_resolver = r self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) @@ -3594,7 +3592,6 @@ external dependencies (including libraries) must go to "dependencies".''') def get_subproject_dep(self, name, display_name, subp_name, varname, kwargs): required = kwargs.get('required', True) wanted = mesonlib.stringlistify(kwargs.get('version', [])) - subproj_path = os.path.join(self.subproject_dir, subp_name) dep = self.notfound_dependency() try: subproject = self.subprojects[subp_name] @@ -3609,9 +3606,9 @@ external dependencies (including libraries) must go to "dependencies".''') else: if required: m = 'Subproject {} did not override dependency {}' - raise DependencyException(m.format(subproj_path, display_name)) + raise DependencyException(m.format(subproject.subdir, display_name)) mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproj_path), 'found:', mlog.red('NO')) + mlog.bold(subproject.subdir), 'found:', mlog.red('NO')) return self.notfound_dependency() if subproject.found(): self.verify_fallback_consistency(subp_name, varname, cached_dep) @@ -3629,7 +3626,7 @@ external dependencies (including libraries) must go to "dependencies".''') ''.format(varname, subp_name)) # If the dependency is not required, don't raise an exception mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproj_path), 'found:', mlog.red('NO')) + mlog.bold(subproject.subdir), 'found:', mlog.red('NO')) return dep found = dep.held_object.get_version() @@ -3640,14 +3637,14 @@ external dependencies (including libraries) must go to "dependencies".''') 'dep {}'.format(found, subp_name, wanted, display_name)) mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproj_path), 'found:', mlog.red('NO'), + mlog.bold(subproject.subdir), 'found:', mlog.red('NO'), 'found', mlog.normal_cyan(found), 'but need:', mlog.bold(', '.join(["'{}'".format(e) for e in wanted]))) return self.notfound_dependency() found = mlog.normal_cyan(found) if found else None mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproj_path), 'found:', mlog.green('YES'), found) + mlog.bold(subproject.subdir), 'found:', mlog.green('YES'), found) return dep def _handle_featurenew_dependencies(self, name): @@ -3798,23 +3795,6 @@ external dependencies (including libraries) must go to "dependencies".''') def func_disabler(self, node, args, kwargs): return Disabler() - def print_nested_info(self, dependency_name): - message = ['Dependency', mlog.bold(dependency_name), 'not found but it is available in a sub-subproject.\n' + - 'To use it in the current project, promote it by going in the project source\n' - 'root and issuing'] - sprojs = mesonlib.detect_subprojects('subprojects', self.source_root) - if dependency_name not in sprojs: - return - found = sprojs[dependency_name] - if len(found) > 1: - message.append('one of the following commands:') - else: - message.append('the following command:') - command_templ = '\nmeson wrap promote {}' - for l in found: - message.append(mlog.bold(command_templ.format(l[len(self.source_root) + 1:]))) - mlog.warning(*message, location=self.current_node) - def get_subproject_infos(self, fbinfo): fbinfo = mesonlib.stringlistify(fbinfo) if len(fbinfo) == 1: @@ -4926,14 +4906,8 @@ This will become a hard error in the future.''', location=self.current_node) # Only permit object extraction from the same subproject def validate_extraction(self, buildtarget: InterpreterObject) -> None: - if not self.subdir.startswith(self.subproject_dir): - if buildtarget.subdir.startswith(self.subproject_dir): - raise InterpreterException('Tried to extract objects from a subproject target.') - else: - if not buildtarget.subdir.startswith(self.subproject_dir): - raise InterpreterException('Tried to extract objects from the main project from a subproject.') - if self.subdir.split('/')[1] != buildtarget.subdir.split('/')[1]: - raise InterpreterException('Tried to extract objects from a different subproject.') + if self.subproject != buildtarget.subproject: + raise InterpreterException('Tried to extract objects from a different subproject.') def is_subproject(self): return self.subproject != '' diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index b628a47..20639cb 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -364,7 +364,7 @@ def run(options): if not os.path.isdir(subprojects_dir): mlog.log('Directory', mlog.bold(src_dir), 'does not seem to have subprojects.') return 0 - r = Resolver(subprojects_dir) + r = Resolver(src_dir, 'subprojects') if options.subprojects: wraps = [wrap for name, wrap in r.wraps.items() if name in options.subprojects] else: diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index a0a4801..8380661 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -97,11 +97,11 @@ class PackageDefinition: self.provided_deps = {} # type: T.Dict[str, T.Optional[str]] self.provided_programs = [] # type: T.List[str] self.basename = os.path.basename(fname) - self.name = self.basename - if self.name.endswith('.wrap'): - self.name = self.name[:-5] + self.has_wrap = self.basename.endswith('.wrap') + self.name = self.basename[:-5] if self.has_wrap else self.basename + self.directory = self.name self.provided_deps[self.name] = None - if fname.endswith('.wrap'): + if self.has_wrap: self.parse_wrap(fname) self.directory = self.values.get('directory', self.name) if os.path.dirname(self.directory): @@ -164,9 +164,11 @@ def get_directory(subdir_root: str, packagename: str) -> str: return packagename class Resolver: - def __init__(self, subdir_root: str, wrap_mode: WrapMode = WrapMode.default) -> None: + def __init__(self, source_dir: str, subdir: str, wrap_mode: WrapMode = WrapMode.default) -> None: + self.source_dir = source_dir + self.subdir = subdir self.wrap_mode = wrap_mode - self.subdir_root = subdir_root + self.subdir_root = os.path.join(source_dir, subdir) self.cachedir = os.path.join(self.subdir_root, 'packagecache') self.filesdir = os.path.join(self.subdir_root, 'packagefiles') self.wraps = {} # type: T.Dict[str, PackageDefinition] @@ -208,6 +210,14 @@ class Resolver: raise WrapException(m.format(k, wrap.basename, prev_wrap.basename)) self.provided_programs[k] = wrap + def merge_wraps(self, other_resolver: 'Resolver') -> None: + for k, v in other_resolver.wraps.items(): + self.wraps.setdefault(k, v) + for k, v in other_resolver.provided_deps.items(): + self.provided_deps.setdefault(k, v) + for k, v in other_resolver.provided_programs.items(): + self.provided_programs.setdefault(k, v) + def find_dep_provider(self, packagename: str) -> T.Optional[T.Union[str, T.List[str]]]: # Return value is in the same format as fallback kwarg: # ['subproject_name', 'variable_name'], or 'subproject_name'. @@ -235,7 +245,24 @@ class Resolver: m = 'Subproject directory not found and {}.wrap file not found' raise WrapNotFoundException(m.format(self.packagename)) self.directory = self.wrap.directory - self.dirname = os.path.join(self.subdir_root, self.directory) + + if self.wrap.has_wrap: + # We have a .wrap file, source code will be placed into main + # project's subproject_dir even if the wrap file comes from another + # subproject. + self.dirname = os.path.join(self.subdir_root, self.directory) + # Copy .wrap file into main project's subproject_dir + wrap_dir = os.path.normpath(os.path.dirname(self.wrap.filename)) + main_dir = os.path.normpath(self.subdir_root) + if wrap_dir != main_dir: + rel = os.path.relpath(self.wrap.filename, self.source_dir) + mlog.log('Using', mlog.bold(rel)) + shutil.copy2(self.wrap.filename, self.subdir_root) + else: + # No wrap file, it's a dummy package definition for an existing + # directory. Use the source code in place. + self.dirname = self.wrap.filename + rel_path = os.path.relpath(self.dirname, self.source_dir) meson_file = os.path.join(self.dirname, 'meson.build') cmake_file = os.path.join(self.dirname, 'CMakeLists.txt') @@ -245,9 +272,9 @@ class Resolver: # The directory is there and has meson.build? Great, use it. if method == 'meson' and os.path.exists(meson_file): - return self.directory + return rel_path if method == 'cmake' and os.path.exists(cmake_file): - return self.directory + return rel_path # Check if the subproject is a git submodule self.resolve_git_submodule() @@ -276,7 +303,7 @@ class Resolver: if method == 'cmake' and not os.path.exists(cmake_file): raise WrapException('Subproject exists but has no CMakeLists.txt file') - return self.directory + return rel_path def check_can_download(self) -> None: # Don't download subproject data based on wrap file if requested. -- cgit v1.1 From 8281bd9e23aacf86adebec5e50a13f9bd344aec1 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 22 Apr 2020 12:20:10 -0400 Subject: interpreter: Improve message when fallback dependency is not found - Log the message before raising the exception. - Add a reason when the dependency is not found because the subproject failed to configure, because it was not obvious in the case the subproject failed to configure earlier while looking for an optional dependency. - Avoid double message when the subproject has overriden the dependency and we provided the fallback variable as well. --- mesonbuild/interpreter.py | 80 ++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 32 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 5104af6..56bc72d 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2169,7 +2169,7 @@ class MesonMain(InterpreterObject): self.interpreter.environment.build_dir) if not os.path.exists(abspath): raise InterpreterException('Tried to override %s with a file that does not exist.' % name) - exe = OverrideProgram(abspath) + exe = OverrideProgram(name, abspath) if not isinstance(exe, (dependencies.ExternalProgram, build.Executable)): raise InterpreterException('Second argument must be an external program or executable.') self.interpreter.add_find_program_override(name, exe) @@ -3374,7 +3374,7 @@ external dependencies (including libraries) must go to "dependencies".''') return ExternalProgramHolder(prog, self.subproject) return None - def program_from_system(self, args, search_dirs, silent=False): + def program_from_system(self, args, search_dirs, extra_info): # Search for scripts relative to current subdir. # Do not cache found programs because find_program('foobar') # might give different results when run from different source dirs. @@ -3397,9 +3397,10 @@ external dependencies (including libraries) must go to "dependencies".''') 'files, not {!r}'.format(exename)) extprog = dependencies.ExternalProgram(exename, search_dir=search_dir, extra_search_dirs=extra_search_dirs, - silent=silent) + silent=True) progobj = ExternalProgramHolder(extprog, self.subproject) if progobj.found(): + extra_info.append('({})'.format(' '.join(progobj.get_command()))) return progobj def program_from_overrides(self, command_names, extra_info): @@ -3457,7 +3458,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not is_found: mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), 'found', mlog.normal_cyan(version), 'but need:', - mlog.bold(', '.join(["'{}'".format(e) for e in not_found]))) + mlog.bold(', '.join(["'{}'".format(e) for e in not_found])), *extra_info) if required: m = 'Invalid version of program, need {!r} {!r} found {!r}.' raise InterpreterException(m.format(progobj.get_name(), not_found, version)) @@ -3483,7 +3484,7 @@ external dependencies (including libraries) must go to "dependencies".''') progobj = self.program_from_file_for(for_machine, args) if progobj is None: - progobj = self.program_from_system(args, search_dirs, silent=True) + progobj = self.program_from_system(args, search_dirs, extra_info) if progobj is None and args[0].endswith('python3'): prog = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True) progobj = ExternalProgramHolder(prog, self.subproject) if prog.found() else None @@ -3593,53 +3594,68 @@ external dependencies (including libraries) must go to "dependencies".''') required = kwargs.get('required', True) wanted = mesonlib.stringlistify(kwargs.get('version', [])) dep = self.notfound_dependency() + + # Verify the subproject is found + subproject = self.subprojects.get(subp_name) + if not subproject or not subproject.found(): + mlog.log('Dependency', mlog.bold(display_name), 'from subproject', + mlog.bold(subproject.subdir), 'found:', mlog.red('NO'), + mlog.blue('(subproject failed to configure)')) + if required: + m = 'Subproject {} failed to configure for dependency {}' + raise DependencyException(m.format(subproject.subdir, display_name)) + return dep + + extra_info = [] try: - subproject = self.subprojects[subp_name] + # Check if the subproject overridden the dependency _, cached_dep = self._find_cached_dep(name, display_name, kwargs) - if varname is None: - # Assuming the subproject overridden the dependency we want - if cached_dep: - if required and not cached_dep.found(): - m = 'Dependency {!r} is not satisfied' - raise DependencyException(m.format(display_name)) - return DependencyHolder(cached_dep, self.subproject) - else: - if required: - m = 'Subproject {} did not override dependency {}' - raise DependencyException(m.format(subproject.subdir, display_name)) - mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproject.subdir), 'found:', mlog.red('NO')) - return self.notfound_dependency() - if subproject.found(): - self.verify_fallback_consistency(subp_name, varname, cached_dep) - dep = self.subprojects[subp_name].get_variable_method([varname], {}) + if cached_dep: + if varname: + self.verify_fallback_consistency(subp_name, varname, cached_dep) + if required and not cached_dep.found(): + m = 'Dependency {!r} is not satisfied' + raise DependencyException(m.format(display_name)) + return DependencyHolder(cached_dep, self.subproject) + elif varname is None: + mlog.log('Dependency', mlog.bold(display_name), 'from subproject', + mlog.bold(subproject.subdir), 'found:', mlog.red('NO')) + if required: + m = 'Subproject {} did not override dependency {}' + raise DependencyException(m.format(subproject.subdir, display_name)) + return self.notfound_dependency() + else: + # The subproject did not override the dependency, but we know the + # variable name to take. + dep = subproject.get_variable_method([varname], {}) except InvalidArguments: - pass + # This is raised by get_variable_method() if varname does no exist + # in the subproject. Just add the reason in the not-found message + # that will be printed later. + extra_info.append(mlog.blue('(Variable {!r} not found)'.format(varname))) if not isinstance(dep, DependencyHolder): raise InvalidCode('Fetched variable {!r} in the subproject {!r} is ' 'not a dependency object.'.format(varname, subp_name)) if not dep.found(): + mlog.log('Dependency', mlog.bold(display_name), 'from subproject', + mlog.bold(subproject.subdir), 'found:', mlog.red('NO'), *extra_info) if required: raise DependencyException('Could not find dependency {} in subproject {}' ''.format(varname, subp_name)) - # If the dependency is not required, don't raise an exception - mlog.log('Dependency', mlog.bold(display_name), 'from subproject', - mlog.bold(subproject.subdir), 'found:', mlog.red('NO')) return dep found = dep.held_object.get_version() if not self.check_version(wanted, found): - if required: - raise DependencyException('Version {} of subproject dependency {} already ' - 'cached, requested incompatible version {} for ' - 'dep {}'.format(found, subp_name, wanted, display_name)) - mlog.log('Dependency', mlog.bold(display_name), 'from subproject', mlog.bold(subproject.subdir), 'found:', mlog.red('NO'), 'found', mlog.normal_cyan(found), 'but need:', mlog.bold(', '.join(["'{}'".format(e) for e in wanted]))) + if required: + raise DependencyException('Version {} of subproject dependency {} already ' + 'cached, requested incompatible version {} for ' + 'dep {}'.format(found, subp_name, wanted, display_name)) return self.notfound_dependency() found = mlog.normal_cyan(found) if found else None -- cgit v1.1 From a20d7ad67d3c876785901349cfc990e4bf31778b Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 29 Aug 2020 18:02:24 -0400 Subject: wrap: Use sub-subproject packagefiles --- mesonbuild/wrap/wrap.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 8380661..ef35c6d 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -90,8 +90,9 @@ class WrapNotFoundException(WrapException): pass class PackageDefinition: - def __init__(self, fname: str): + def __init__(self, fname: str, filesdir: str = None): self.filename = fname + self.filesdir = filesdir self.type = None # type: T.Optional[str] self.values = {} # type: T.Dict[str, str] self.provided_deps = {} # type: T.Dict[str, T.Optional[str]] @@ -184,7 +185,7 @@ class Resolver: if not i.endswith('.wrap'): continue fname = os.path.join(self.subdir_root, i) - wrap = PackageDefinition(fname) + wrap = PackageDefinition(fname, self.filesdir) self.wraps[wrap.name] = wrap if wrap.directory in dirs: dirs.remove(wrap.directory) @@ -193,7 +194,7 @@ class Resolver: if i in ['packagecache', 'packagefiles']: continue fname = os.path.join(self.subdir_root, i) - wrap = PackageDefinition(fname) + wrap = PackageDefinition(fname, self.filesdir) self.wraps[wrap.name] = wrap for wrap in self.wraps.values(): @@ -514,7 +515,7 @@ class Resolver: else: from ..interpreterbase import FeatureNew FeatureNew('Local wrap patch files without {}_url'.format(what), '0.55.0').use(self.current_subproject) - path = Path(self.filesdir) / filename + path = Path(self.wrap.filesdir) / filename if not path.exists(): raise WrapException('File "{}" does not exist'.format(path)) @@ -538,7 +539,7 @@ class Resolver: from ..interpreterbase import FeatureNew FeatureNew('patch_directory', '0.55.0').use(self.current_subproject) patch_dir = self.wrap.values['patch_directory'] - src_dir = os.path.join(self.filesdir, patch_dir) + src_dir = os.path.join(self.wrap.filesdir, patch_dir) if not os.path.isdir(src_dir): raise WrapException('patch directory does not exists: {}'.format(patch_dir)) self.copy_tree(src_dir, self.dirname) -- cgit v1.1 From 173c115834e37acf2a66fd00fa4010e5765c39a9 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Thu, 17 Sep 2020 11:46:42 -0400 Subject: Add wrap mode to disable auto promote --- mesonbuild/interpreter.py | 13 +++++++------ mesonbuild/wrap/__init__.py | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 56bc72d..ad6f04e 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -3166,12 +3166,13 @@ external dependencies (including libraries) must go to "dependencies".''') # Load wrap files from this (sub)project. wrap_mode = self.coredata.get_builtin_option('wrap_mode') - subdir = os.path.join(self.subdir, spdirname) - r = wrap.Resolver(self.environment.get_source_dir(), subdir, wrap_mode) - if self.is_subproject(): - self.environment.wrap_resolver.merge_wraps(r) - else: - self.environment.wrap_resolver = r + if not self.is_subproject() or wrap_mode != WrapMode.nopromote: + subdir = os.path.join(self.subdir, spdirname) + r = wrap.Resolver(self.environment.get_source_dir(), subdir, wrap_mode) + if self.is_subproject(): + self.environment.wrap_resolver.merge_wraps(r) + else: + self.environment.wrap_resolver = r self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) diff --git a/mesonbuild/wrap/__init__.py b/mesonbuild/wrap/__init__.py index 1771146..653f42a 100644 --- a/mesonbuild/wrap/__init__.py +++ b/mesonbuild/wrap/__init__.py @@ -40,6 +40,7 @@ string_to_value = {'default': 1, 'nofallback': 2, 'nodownload': 3, 'forcefallback': 4, + 'nopromote': 5, } class WrapMode(Enum): @@ -47,6 +48,7 @@ class WrapMode(Enum): nofallback = 2 nodownload = 3 forcefallback = 4 + nopromote = 5 def __str__(self) -> str: return self.name -- cgit v1.1 From 3a0182378612bc764667328805b11bc92db696c2 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 29 Sep 2020 22:24:56 -0400 Subject: wrap: Add 'redirect' type and use it when auto promote --- mesonbuild/wrap/wrap.py | 52 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index ef35c6d..a4e0b25 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -26,6 +26,7 @@ import subprocess import sys import configparser import typing as T +import textwrap from .._pathlib import Path from . import WrapMode @@ -90,9 +91,8 @@ class WrapNotFoundException(WrapException): pass class PackageDefinition: - def __init__(self, fname: str, filesdir: str = None): + def __init__(self, fname: str): self.filename = fname - self.filesdir = filesdir self.type = None # type: T.Optional[str] self.values = {} # type: T.Dict[str, str] self.provided_deps = {} # type: T.Dict[str, T.Optional[str]] @@ -103,20 +103,42 @@ class PackageDefinition: self.directory = self.name self.provided_deps[self.name] = None if self.has_wrap: - self.parse_wrap(fname) + self.parse_wrap() self.directory = self.values.get('directory', self.name) if os.path.dirname(self.directory): raise WrapException('Directory key must be a name and not a path') if self.type and self.type not in ALL_TYPES: raise WrapException('Unknown wrap type {!r}'.format(self.type)) + self.filesdir = os.path.join(os.path.dirname(self.filename), 'packagefiles') - def parse_wrap(self, fname: str) -> None: + def parse_wrap(self) -> None: try: self.config = configparser.ConfigParser(interpolation=None) - self.config.read(fname) + self.config.read(self.filename) except configparser.Error: raise WrapException('Failed to parse {}'.format(self.basename)) self.parse_wrap_section() + if self.type == 'redirect': + # [wrap-redirect] have a `filename` value pointing to the real wrap + # file we should parse instead. It must be relative to the current + # wrap file location and must be in the form foo/subprojects/bar.wrap. + dirname = Path(self.filename).parent + fname = Path(self.values['filename']) + for i, p in enumerate(fname.parts): + if i % 2 == 0: + if p == '..': + raise WrapException('wrap-redirect filename cannot contain ".."') + else: + if p != 'subprojects': + raise WrapException('wrap-redirect filename must be in the form foo/subprojects/bar.wrap') + if fname.suffix != '.wrap': + raise WrapException('wrap-redirect filename must be a .wrap file') + fname = dirname / fname + if not fname.is_file(): + raise WrapException('wrap-redirect filename does not exist') + self.filename = str(fname) + self.parse_wrap() + return self.parse_provide_section() def parse_wrap_section(self) -> None: @@ -171,7 +193,6 @@ class Resolver: self.wrap_mode = wrap_mode self.subdir_root = os.path.join(source_dir, subdir) self.cachedir = os.path.join(self.subdir_root, 'packagecache') - self.filesdir = os.path.join(self.subdir_root, 'packagefiles') self.wraps = {} # type: T.Dict[str, PackageDefinition] self.provided_deps = {} # type: T.Dict[str, PackageDefinition] self.provided_programs = {} # type: T.Dict[str, PackageDefinition] @@ -185,7 +206,7 @@ class Resolver: if not i.endswith('.wrap'): continue fname = os.path.join(self.subdir_root, i) - wrap = PackageDefinition(fname, self.filesdir) + wrap = PackageDefinition(fname) self.wraps[wrap.name] = wrap if wrap.directory in dirs: dirs.remove(wrap.directory) @@ -194,7 +215,7 @@ class Resolver: if i in ['packagecache', 'packagefiles']: continue fname = os.path.join(self.subdir_root, i) - wrap = PackageDefinition(fname, self.filesdir) + wrap = PackageDefinition(fname) self.wraps[wrap.name] = wrap for wrap in self.wraps.values(): @@ -252,13 +273,18 @@ class Resolver: # project's subproject_dir even if the wrap file comes from another # subproject. self.dirname = os.path.join(self.subdir_root, self.directory) - # Copy .wrap file into main project's subproject_dir - wrap_dir = os.path.normpath(os.path.dirname(self.wrap.filename)) - main_dir = os.path.normpath(self.subdir_root) - if wrap_dir != main_dir: + # Check if the wrap comes from the main project. + main_fname = os.path.join(self.subdir_root, self.wrap.basename) + if self.wrap.filename != main_fname: rel = os.path.relpath(self.wrap.filename, self.source_dir) mlog.log('Using', mlog.bold(rel)) - shutil.copy2(self.wrap.filename, self.subdir_root) + # Write a dummy wrap file in main project that redirect to the + # wrap we picked. + with open(main_fname, 'w') as f: + f.write(textwrap.dedent('''\ + [wrap-redirect] + filename = {} + '''.format(os.path.relpath(self.wrap.filename, self.subdir_root)))) else: # No wrap file, it's a dummy package definition for an existing # directory. Use the source code in place. -- cgit v1.1