Skip to content

Commit

Permalink
[WIP] Fix subprojects logic in cross-scenarios
Browse files Browse the repository at this point in the history
  • Loading branch information
oleavr committed Feb 6, 2024
1 parent 7f80556 commit d4ddf9a
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 30 deletions.
20 changes: 12 additions & 8 deletions mesonbuild/interpreter/dependencyfallbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _do_dependency_cache(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_
name = func_args[0]
cached_dep = self._get_cached_dep(name, kwargs)
if cached_dep:
self._verify_fallback_consistency(cached_dep)
self._verify_fallback_consistency(cached_dep, kwargs.get('native', False))
return cached_dep

def _do_dependency(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
Expand All @@ -95,7 +95,7 @@ def _do_dependency(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs
def _do_existing_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
subp_name = func_args[0]
varname = self.subproject_varname
if subp_name and self._get_subproject(subp_name):
if subp_name and self._get_subproject(subp_name, kwargs.get('native', False)):
return self._get_subproject_dep(subp_name, varname, kwargs)
return None

Expand Down Expand Up @@ -130,15 +130,17 @@ def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs
self.interpreter.do_subproject(subp_name, func_kwargs)
return self._get_subproject_dep(subp_name, varname, kwargs)

def _get_subproject(self, subp_name: str) -> T.Optional[SubprojectHolder]:
sub = self.interpreter.subprojects.get(subp_name)
def _get_subproject(self, subp_name: str, native: bool) -> T.Optional[SubprojectHolder]:
sub = self.interpreter.find_subproject(subp_name, native)
if sub and sub.found():
return sub
return None

def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
native = kwargs.get('native', False)

# Verify the subproject is found
subproject = self._get_subproject(subp_name)
subproject = self._get_subproject(subp_name, native)
if not subproject:
mlog.log('Dependency', mlog.bold(self._display_name), 'from subproject',
mlog.bold(subp_name), 'found:', mlog.red('NO'),
Expand All @@ -160,7 +162,7 @@ def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: TYPE_nkwargs
# If we have cached_dep we did all the checks and logging already in
# self._get_cached_dep().
if cached_dep:
self._verify_fallback_consistency(cached_dep)
self._verify_fallback_consistency(cached_dep, native)
return cached_dep

# Legacy: Use the variable name if provided instead of relying on the
Expand Down Expand Up @@ -256,10 +258,12 @@ def _get_subproject_variable(self, subproject: SubprojectHolder, varname: str) -
return None
return var_dep

def _verify_fallback_consistency(self, cached_dep: Dependency) -> None:
def _verify_fallback_consistency(self, cached_dep: Dependency, native: bool) -> None:
subp_name = self.subproject_name
if subp_name is None:
return
varname = self.subproject_varname
subproject = self._get_subproject(subp_name)
subproject = self._get_subproject(subp_name, native)
if subproject and varname:
var_dep = self._get_subproject_variable(subproject, varname)
if var_dep and cached_dep.found() and var_dep != cached_dep:
Expand Down
57 changes: 35 additions & 22 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,18 +859,29 @@ def func_subproject(self, nodes: mparser.BaseNode, args: T.Tuple[str], kwargs: k
}
return self.do_subproject(args[0], kw)

def disabled_subproject(self, subp_name: str, disabled_feature: T.Optional[str] = None,
def find_subproject(self, subp_name: str, native: bool) -> Optional[SubprojectHolder]:
return self.subprojects.get(self._make_subproject_id(subp_name, native))

def _make_subproject_id(self, subp_name: str, native: bool) -> str:
if not self.coredata.is_cross_build():
return subp_name
tag = 'build' if native else 'host'
return ':'.join([subp_name, tag])

def disabled_subproject(self, subp_name: str, subp_id: str, disabled_feature: T.Optional[str] = None,
exception: T.Optional[Exception] = None) -> SubprojectHolder:
sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name),
disabled_feature=disabled_feature, exception=exception)
self.subprojects[subp_name] = sub
self.subprojects[subp_id] = sub
return sub

def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None) -> SubprojectHolder:
subp_id = self._make_subproject_id(subp_name, kwargs.get('native', False))

disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
return self.disabled_subproject(subp_name, disabled_feature=feature)
return self.disabled_subproject(subp_name, subp_id, disabled_feature=feature)

default_options = {k.evolve(subproject=subp_name): v for k, v in kwargs['default_options'].items()}

Expand All @@ -889,8 +900,8 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth
fullstack = self.subproject_stack + [subp_name]
incpath = ' => '.join(fullstack)
raise InvalidCode(f'Recursive include of subprojects: {incpath}.')
if subp_name in self.subprojects:
subproject = self.subprojects[subp_name]
if subp_id in self.subprojects:
subproject = self.subprojects[subp_id]
if required and not subproject.found():
raise InterpreterException(f'Subproject "{subproject.subdir}" required but not found.')
if kwargs['version']:
Expand All @@ -907,7 +918,7 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth
if not required:
mlog.log(e)
mlog.log('Subproject ', mlog.bold(subp_name), 'is buildable:', mlog.red('NO'), '(disabling)')
return self.disabled_subproject(subp_name, exception=e)
return self.disabled_subproject(subp_name, subp_id, exception=e)
raise e

os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
Expand All @@ -919,14 +930,14 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth
m += ['method', mlog.bold(method)]
mlog.log(*m, '\n', nested=False)

methods_map: T.Dict[wrap.Method, T.Callable[[str, str, T.Dict[OptionKey, str, kwtypes.DoSubproject]], SubprojectHolder]] = {
methods_map: T.Dict[wrap.Method, T.Callable[[str, str, T.Dict[OptionKey, str, str, kwtypes.DoSubproject]], SubprojectHolder]] = {
'meson': self._do_subproject_meson,
'cmake': self._do_subproject_cmake,
'cargo': self._do_subproject_cargo,
}

try:
return methods_map[method](subp_name, subdir, default_options, kwargs)
return methods_map[method](subp_name, subp_id, subdir, default_options, kwargs)
# Invalid code is always an error
except InvalidCode:
raise
Expand All @@ -937,15 +948,17 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth
# fatal and VS CI treat any logs with "ERROR:" as fatal.
mlog.exception(e, prefix=mlog.yellow('Exception:'))
mlog.log('\nSubproject', mlog.bold(subdir), 'is buildable:', mlog.red('NO'), '(disabling)')
return self.disabled_subproject(subp_name, exception=e)
return self.disabled_subproject(subp_name, subp_id, exception=e)
raise e

def _do_subproject_meson(self, subp_name: str, subdir: str,
def _do_subproject_meson(self, subp_name: str, subp_id: str, subdir: str,
default_options: T.Dict[OptionKey, str],
kwargs: kwtypes.DoSubproject,
ast: T.Optional[mparser.CodeBlockNode] = None,
build_def_files: T.Optional[T.List[str]] = None,
relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None) -> SubprojectHolder:
subp_id = self._make_subproject_id(subp_name, kwargs.get('native', False))

with mlog.nested(subp_name):
if ast:
# Debug print the generated meson file
Expand All @@ -961,7 +974,7 @@ def _do_subproject_meson(self, subp_name: str, subdir: str,
mlog.cmd_ci_include(meson_filename)

new_build = self.build.copy()
subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir,
subi = Interpreter(new_build, self.backend, subp_id, subdir, self.subproject_dir,
default_options, ast=ast, is_translated=(ast is not None),
relaxations=relaxations,
user_defined_options=self.user_defined_options)
Expand Down Expand Up @@ -990,18 +1003,18 @@ def _do_subproject_meson(self, subp_name: str, subdir: str,
raise InterpreterException(f'Subproject {subp_name} version is {pv} but {wanted} required.')
self.active_projectname = current_active
self.subprojects.update(subi.subprojects)
self.subprojects[subp_name] = SubprojectHolder(subi, subdir, warnings=subi_warnings,
callstack=self.subproject_stack)
self.subprojects[subp_id] = SubprojectHolder(subi, subdir, warnings=subi_warnings,
callstack=self.subproject_stack)
# Duplicates are possible when subproject uses files from project root
if build_def_files:
self.build_def_files.update(build_def_files)
# We always need the subi.build_def_files, to propagate sub-sub-projects
self.build_def_files.update(subi.build_def_files)
self.build.merge(subi.build)
self.build.subprojects[subp_name] = subi.project_version
return self.subprojects[subp_name]
return self.subprojects[subp_id]

def _do_subproject_cmake(self, subp_name: str, subdir: str,
def _do_subproject_cmake(self, subp_name: str, subp_id: str, subdir: str,
default_options: T.Dict[OptionKey, str],
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from ..cmake import CMakeInterpreter
Expand All @@ -1018,7 +1031,7 @@ def _do_subproject_cmake(self, subp_name: str, subdir: str,
# Generate a meson ast and execute it with the normal do_subproject_meson
ast = cm_int.pretend_to_be_meson(options.target_options)
result = self._do_subproject_meson(
subp_name, subdir, default_options,
subp_name, subp_id, subdir, default_options,
kwargs, ast,
[str(f) for f in cm_int.bs_files],
relaxations={
Expand All @@ -1028,15 +1041,15 @@ def _do_subproject_cmake(self, subp_name: str, subdir: str,
result.cm_interpreter = cm_int
return result

def _do_subproject_cargo(self, subp_name: str, subdir: str,
def _do_subproject_cargo(self, subp_name: str, subp_id: str, subdir: str,
default_options: T.Dict[OptionKey, str],
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from .. import cargo
FeatureNew.single_use('Cargo subproject', '1.3.0', self.subproject, location=self.current_node)
with mlog.nested(subp_name):
ast = cargo.interpret(subp_name, subdir, self.environment)
return self._do_subproject_meson(
subp_name, subdir, default_options, kwargs, ast,
subp_name, subp_id, subdir, default_options, kwargs, ast,
# FIXME: Are there other files used by cargo interpreter?
[os.path.join(subdir, 'Cargo.toml')])

Expand Down Expand Up @@ -1372,7 +1385,7 @@ def summary_impl(self, section: str, values, kwargs: 'kwtypes.Summary') -> None:
def _print_summary(self) -> None:
# Add automatic 'Subprojects' section in main project.
all_subprojects = collections.OrderedDict()
for name, subp in sorted(self.subprojects.items()):
for subp_id, subp in sorted(self.subprojects.items()):
value = [subp.found()]
if subp.disabled_feature:
value += [f'Feature {subp.disabled_feature!r} disabled']
Expand All @@ -1383,7 +1396,7 @@ def _print_summary(self) -> None:
if subp.callstack:
stack = ' => '.join(subp.callstack)
value += [f'(from {stack})']
all_subprojects[name] = value
all_subprojects[subp_id] = value
if all_subprojects:
self.summary_impl('Subprojects', all_subprojects,
{'bool_yn': True,
Expand All @@ -1403,8 +1416,8 @@ def _print_summary(self) -> None:
# Print all summaries, main project last.
mlog.log('') # newline
main_summary = self.summary.pop('', None)
for subp_name, summary in sorted(self.summary.items()):
if self.subprojects[subp_name].found():
for subp_id, summary in sorted(self.summary.items()):
if self.subprojects[subp_id].found():
summary.dump()
if main_summary:
main_summary.dump()
Expand Down
1 change: 1 addition & 0 deletions mesonbuild/wrap/wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ def find_dep_provider(self, packagename: str) -> T.Tuple[T.Optional[str], T.Opti

def get_varname(self, subp_name: str, depname: str) -> T.Optional[str]:
wrap = self.wraps.get(subp_name)
print("got wrap:", wrap)
return wrap.provided_deps.get(depname) if wrap else None

def find_program_provider(self, names: T.List[str]) -> T.Optional[str]:
Expand Down

0 comments on commit d4ddf9a

Please sign in to comment.