diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 934e7c1f01ab..fde07be80add 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -291,12 +291,22 @@ def get_custom_targets(self): def copy(self) -> Build: other = Build(self.environment) + self._copy_to(other) + return other + + def copy_to_native(self) -> Build: + other = Build(self.environment.copy_to_native()) + self._copy_to(other) + return other + + def _copy_to(self, other: Build) -> None: for k, v in self.__dict__.items(): + if k == 'environment': + continue if isinstance(v, (list, dict, set, OrderedDict)): other.__dict__[k] = v.copy() else: other.__dict__[k] = v - return other def merge(self, other: Build) -> None: for k, v in other.__dict__.items(): @@ -307,7 +317,7 @@ def ensure_static_linker(self, compiler: Compiler) -> None: self.static_linker[compiler.for_machine] = detect_static_linker(self.environment, compiler) def get_project(self): - return self.projects[''] + return self.projects[('', MachineChoice.HOST)] def get_subproject_dir(self): return self.subproject_dir @@ -596,7 +606,7 @@ def get_basename(self) -> str: return self.name def get_subdir(self) -> str: - return self.subdir + return self.environment.build_output_rpath(self.subdir) def get_typename(self) -> str: return self.typename @@ -612,7 +622,7 @@ def _get_id_hash(target_id: str) -> str: return h.hexdigest()[:7] @staticmethod - def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str: + def construct_id_from_path(subdir: str, name: str, type_suffix: str, extra_suffix: str = '') -> str: """Construct target ID from subdir, name and type suffix. This helper function is made public mostly for tests.""" @@ -623,7 +633,7 @@ def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str: # FIXME replace with assert when slash in names is prohibited name_part = name.replace('/', '@').replace('\\', '@') assert not has_path_sep(type_suffix) - my_id = name_part + type_suffix + my_id = name_part + type_suffix + extra_suffix if subdir: subdir_part = Target._get_id_hash(subdir) # preserve myid for better debuggability @@ -635,7 +645,7 @@ def get_id(self) -> str: if getattr(self, 'name_suffix_set', False): name += '.' + self.suffix return self.construct_id_from_path( - self.subdir, name, self.type_suffix()) + self.subdir, name, self.type_suffix(), '@native' if self.environment.is_native_clone else '') def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None: if 'build_by_default' in kwargs: @@ -1481,6 +1491,8 @@ def check_can_link_together(self, t: BuildTargetTypes) -> None: links_with_rust_abi = isinstance(t, BuildTarget) and t.uses_rust_abi() if not self.uses_rust() and links_with_rust_abi: raise InvalidArguments(f'Try to link Rust ABI library {t.name!r} with a non-Rust target {self.name!r}') + if t.subproject and not self.environment.is_cross_build(): + return if self.for_machine is not t.for_machine and (not links_with_rust_abi or t.rust_crate_type != 'proc-macro'): msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}' if self.environment.is_cross_build(): diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index a6a14c8abf56..0df85ca30086 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -586,6 +586,30 @@ def __init__(self, options: argparse.Namespace, scratch_dir: str, meson_command: self.builtin_options_libdir_cross_fixup() self.init_builtins('') + self.is_native_clone = False + + def copy_to_native(self) -> CoreData: + other = CoreData.__new__(CoreData) + for k, v in self.__dict__.items(): + other.__dict__[k] = v + + other.cross_files = [] + + other.compilers = PerMachine(OrderedDict(), OrderedDict()) + other.compilers.build = self.compilers.build + + other.deps = PerMachineDefaultable.default( + is_cross=False, + build=self.deps.build, + host=self.deps.host) + + other.is_native_clone = True + + other.compiler_check_cache.clear() + other.run_check_cache.clear() + + return other + @staticmethod def __load_config_files(options: argparse.Namespace, scratch_dir: str, ftype: str) -> T.List[str]: # Need to try and make the passed filenames absolute because when the @@ -927,7 +951,9 @@ def copy_build_options_from_regular_ones(self) -> bool: def set_options(self, options: T.Dict[OptionKey, T.Any], subproject: str = '', first_invocation: bool = False) -> bool: dirty = False if not self.is_cross_build(): - options = {k: v for k, v in options.items() if k.machine is not MachineChoice.BUILD} + other_machine = MachineChoice.HOST if self.is_native_clone else MachineChoice.BUILD + options = {k: v for k, v in options.items() if k.machine is not other_machine} + # Set prefix first because it's needed to sanitize other options pfk = OptionKey('prefix') if pfk in options: @@ -950,7 +976,7 @@ def set_options(self, options: T.Dict[OptionKey, T.Any], subproject: str = '', f sub = f'In subproject {subproject}: ' if subproject else '' raise MesonException(f'{sub}Unknown options: "{unknown_options_str}"') - if not self.is_cross_build(): + if not self.is_cross_build() and not self.is_native_clone: dirty |= self.copy_build_options_from_regular_ones() return dirty diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 326a1c9ede52..682e45c7b1e3 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -620,6 +620,37 @@ def __init__(self, source_dir: str, build_dir: str, options: 'argparse.Namespace self.default_pkgconfig = ['pkg-config'] self.wrap_resolver: T.Optional['Resolver'] = None + self.is_native_clone = False + + def copy_to_native(self) -> Environment: + other = Environment.__new__(Environment) + for k, v in self.__dict__.items(): + other.__dict__[k] = v + + other.coredata = self.coredata.copy_to_native() + + machines: PerThreeMachineDefaultable[MachineInfo] = PerThreeMachineDefaultable() + machines.build = self.machines.build + other.machines = machines.default_missing() + + binaries: PerMachineDefaultable[BinaryTable] = PerMachineDefaultable() + binaries.build = self.binaries.build + other.binaries = binaries.default_missing() + + properties: PerMachineDefaultable[Properties] = PerMachineDefaultable() + properties.build = self.properties.build + other.properties = properties.default_missing() + + cmakevars: PerMachineDefaultable[CMakeVariables] = PerMachineDefaultable() + cmakevars.build = self.cmakevars.build + other.cmakevars = cmakevars.default_missing() + + other.exe_wrapper = None + + other.is_native_clone = True + + return other + def _load_machine_file_options(self, config: 'ConfigParser', properties: Properties, machine: MachineChoice) -> None: """Read the contents of a Machine file and put it in the options store.""" @@ -841,6 +872,12 @@ def get_source_dir(self) -> str: def get_build_dir(self) -> str: return self.build_dir + def build_output_rpath(self, subdir: str, *parts: T.Sequence[str]) -> str: + result = subdir + if self.is_native_clone: + result += '-native' + return os.path.join(result, *parts) + def get_import_lib_dir(self) -> str: "Install dir for the import library (library used for linking)" return self.get_libdir() diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index eca6a2c71796..239d5e37ec16 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -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]: @@ -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 @@ -127,18 +127,21 @@ def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs func_kwargs.setdefault('version', []) if 'default_options' in kwargs and isinstance(kwargs['default_options'], str): func_kwargs['default_options'] = listify(kwargs['default_options']) + func_kwargs.setdefault('native', kwargs.get('native', False)) 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'), @@ -160,7 +163,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 @@ -256,10 +259,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: @@ -336,7 +341,8 @@ def lookup(self, kwargs: TYPE_nkwargs, force_fallback: bool = False) -> Dependen subp_name, varname = self.wrap_resolver.find_dep_provider(name) if subp_name: self.forcefallback |= subp_name in force_fallback_for - if self.forcefallback or self.allow_fallback is True or required or self._get_subproject(subp_name): + if self.forcefallback or self.allow_fallback is True or required \ + or self._get_subproject(subp_name, kwargs.get('native', False)): self._subproject_impl(subp_name, varname) break diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bee64aa94ba4..477888d0ec72 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -172,13 +172,26 @@ def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool, self.sections[section][k] = (formatted_values, list_sep) self.max_key_len = max(self.max_key_len, len(k)) - def dump(self): - mlog.log(self.project_name, mlog.normal_cyan(self.project_version)) + def dump(self, subp_id: T.Tuple[str, MachineChoice], is_cross_build: bool): + heading = [self.project_name, mlog.normal_cyan(self.project_version)] + if is_cross_build: + subp_name, for_machine = subp_id + if subp_name and for_machine is MachineChoice.BUILD: + heading += [mlog.normal_green('(native)')] + mlog.log(*heading) for section, values in self.sections.items(): mlog.log('') # newline if section: mlog.log(' ', mlog.bold(section)) - for k, v in values.items(): + for raw_k, v in values.items(): + if isinstance(raw_k, tuple): + subp_name, for_machine = raw_k + if is_cross_build and for_machine is MachineChoice.BUILD: + k = f'{subp_name}(native)' + else: + k = subp_name + else: + k = raw_k v, list_sep = v padding = self.max_key_len - len(k) end = ' ' if v else '' @@ -262,7 +275,8 @@ def __init__( self, _build: build.Build, backend: T.Optional[Backend] = None, - subproject: str = '', + subp_name: str = '', + subp_id: T.Tuple[str, MachineChoice] = ('', MachineChoice.HOST), subdir: str = '', subproject_dir: str = 'subprojects', default_project_options: T.Optional[T.Dict[OptionKey, str]] = None, @@ -271,7 +285,8 @@ def __init__( relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None, user_defined_options: T.Optional['argparse.Namespace'] = None, ) -> None: - super().__init__(_build.environment.get_source_dir(), subdir, subproject) + super().__init__(_build.environment.get_source_dir(), subdir, subp_name) + self.active_projectname = '' self.build = _build self.environment = self.build.environment @@ -279,6 +294,7 @@ def __init__( self.backend = backend self.summary: T.Dict[str, 'Summary'] = {} self.modules: T.Dict[str, NewExtensionModule] = {} + self.subp_id = subp_id # Subproject directory is usually the name of the subproject, but can # be different for dependencies provided by wrap files. self.subproject_directory_name = subdir.split(os.path.sep)[-1] @@ -294,7 +310,7 @@ def __init__( self.processed_buildfiles: T.Set[str] = set() self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen - self.subprojects: T.Dict[str, SubprojectHolder] = {} + self.subprojects: T.Dict[T.Tuple[str, MachineChoice], SubprojectHolder] = {} self.subproject_stack: T.List[str] = [] self.configure_file_outputs: T.Dict[str, int] = {} # Passed from the outside, only used in subprojects. @@ -859,18 +875,28 @@ 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) -> T.Optional[SubprojectHolder]: + return self.subprojects.get(self._make_subproject_id(subp_name, native)) + + def _make_subproject_id(self, subp_name: str, native: bool) -> T.Tuple[str, MachineChoice]: + for_machine = MachineChoice.BUILD if native else MachineChoice.HOST + return (subp_name, for_machine) + + 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: + native = kwargs.get('native', False) + subp_id = self._make_subproject_id(subp_name, native) + 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()} @@ -889,8 +915,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']: @@ -907,7 +933,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) @@ -915,18 +941,20 @@ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_meth stack = ':'.join(self.subproject_stack + [subp_name]) m = ['\nExecuting subproject', mlog.bold(stack)] + if native and self.coredata.is_cross_build(): + m += [mlog.normal_green('(native)')] if method != 'meson': 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 @@ -937,15 +965,18 @@ 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: + native = kwargs.get('native', False) + subp_id = self._make_subproject_id(subp_name, native) + with mlog.nested(subp_name): if ast: # Debug print the generated meson file @@ -960,8 +991,12 @@ def _do_subproject_meson(self, subp_name: str, subdir: str, mlog.log('Generated Meson AST:', meson_filename) mlog.cmd_ci_include(meson_filename) - new_build = self.build.copy() - subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, + if native and self.coredata.is_cross_build(): + new_build = self.build.copy_to_native() + default_options = {k.as_build(): v for k, v in default_options.items()} + else: + new_build = self.build.copy() + subi = Interpreter(new_build, self.backend, subp_name, 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) @@ -990,8 +1025,8 @@ 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) @@ -999,9 +1034,9 @@ def _do_subproject_meson(self, subp_name: str, subdir: str, 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 @@ -1018,7 +1053,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={ @@ -1028,7 +1063,7 @@ 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 @@ -1036,13 +1071,16 @@ def _do_subproject_cargo(self, subp_name: str, subdir: str, 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')]) def get_option_internal(self, optname: str) -> coredata.UserOption: key = OptionKey.from_string(optname).evolve(subproject=self.subproject) + if self.environment.is_native_clone: + key = key.as_build() + if not key.is_project(): for opts in [self.coredata.options, compilers.base_options]: v = opts.get(key) @@ -1188,7 +1226,10 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str if os.path.exists(option_file): oi = optinterpreter.OptionInterpreter(self.subproject) oi.process(option_file) - self.coredata.update_project_options(oi.options) + opts = oi.options + if self.environment.is_native_clone: + opts = {k.as_build(): v for k, v in opts.items()} + self.coredata.update_project_options(opts) self.add_build_def_file(option_file) if self.subproject: @@ -1252,7 +1293,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str proj_license_files.append((ifname, i)) self.build.dep_manifest[proj_name] = build.DepManifest(self.project_version, proj_license, proj_license_files, self.subproject) - if self.subproject in self.build.projects: + if self.subp_id in self.build.projects: raise InvalidCode('Second call to project().') # spdirname is the subproject_dir for this project, relative to self.subdir. @@ -1280,7 +1321,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str else: self.environment.wrap_resolver = r - self.build.projects[self.subproject] = proj_name + self.build.projects[self.subp_id] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) @@ -1364,15 +1405,15 @@ def func_summary(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, T.Dict self.summary_impl(kwargs['section'], values, kwargs) def summary_impl(self, section: str, values, kwargs: 'kwtypes.Summary') -> None: - if self.subproject not in self.summary: - self.summary[self.subproject] = Summary(self.active_projectname, self.project_version) - self.summary[self.subproject].add_section( + if self.subp_id not in self.summary: + self.summary[self.subp_id] = Summary(self.active_projectname, self.project_version) + self.summary[self.subp_id].add_section( section, values, kwargs['bool_yn'], kwargs['list_sep'], self.subproject) 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'] @@ -1383,7 +1424,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, @@ -1402,12 +1443,14 @@ def _print_summary(self) -> None: self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': 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(): - summary.dump() + main_subp = ('', MachineChoice.HOST) + main_summary = self.summary.pop(main_subp, None) + is_cross_build = self.coredata.is_cross_build() + for subp_id, summary in sorted(self.summary.items()): + if self.subprojects[subp_id].found(): + summary.dump(subp_id, is_cross_build) if main_summary: - main_summary.dump() + main_summary.dump(main_subp, is_cross_build) @noArgsFlattening @FeatureNew('warning', '0.44.0') @@ -1463,7 +1506,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): def add_languages(self, args: T.List[str], required: bool, for_machine: MachineChoice) -> bool: success = self.add_languages_for(args, required, for_machine) - if not self.coredata.is_cross_build(): + if not self.coredata.is_cross_build() and not self.coredata.is_native_clone: self.coredata.copy_build_options_from_regular_ones() self._redetect_machines() return success @@ -1748,8 +1791,8 @@ def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonli search_dirs = extract_search_dirs(kwargs) default_options = kwargs['default_options'] - return self.find_program_impl(args[0], kwargs['native'], default_options=default_options, required=required, - silent=False, wanted=kwargs['version'], + return self.find_program_impl(args[0], self.kwarg_options_to_machine(kwargs), default_options=default_options, + required=required, silent=False, wanted=kwargs['version'], search_dirs=search_dirs) # When adding kwargs, please check if they make sense in dependencies.get_dep_identifier() @@ -2621,7 +2664,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], output = outputs[0] if depfile: depfile = mesonlib.substitute_values([depfile], values)[0] - ofile_rpath = os.path.join(self.subdir, output) + ofile_rpath = self.environment.build_output_rpath(self.subdir, output) if ofile_rpath in self.configure_file_outputs: mesonbuildfile = os.path.join(self.subdir, 'meson.build') current_call = f"{mesonbuildfile}:{self.current_lineno}" @@ -2629,9 +2672,11 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], mlog.warning('Output file', mlog.bold(ofile_rpath, True), 'for configure_file() at', current_call, 'overwrites configure_file() output at', first_call) else: self.configure_file_outputs[ofile_rpath] = self.current_lineno - (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output)) + (ofile_path, ofile_fname) = os.path.split(ofile_rpath) ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname) + output_subdir = self.environment.build_output_rpath(self.subdir) + # Perform the appropriate action if kwargs['configuration'] is not None: conf = kwargs['configuration'] @@ -2645,8 +2690,8 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], mlog.log('Configuring', mlog.bold(output), 'using configuration') if len(inputs) > 1: raise InterpreterException('At most one input file can given in configuration mode') + os.makedirs(os.path.join(self.environment.build_dir, output_subdir), exist_ok=True) if inputs: - os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True) file_encoding = kwargs['encoding'] missing_variables, confdata_useless = \ mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf, @@ -2704,7 +2749,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], elif kwargs['copy']: if len(inputs_abs) != 1: raise InterpreterException('Exactly one input file must be given in copy mode') - os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True) + os.makedirs(os.path.join(self.environment.build_dir, output_subdir), exist_ok=True) shutil.copy2(inputs_abs[0], ofile_abs) # Install file if requested, we check for the empty string @@ -2727,7 +2772,7 @@ def func_configure_file(self, node: mparser.BaseNode, args: T.List[TYPE_var], install_tag = kwargs['install_tag'] self.build.data.append(build.Data([cfile], idir, idir_name, install_mode, self.subproject, install_tag=install_tag, data_type='configure')) - return mesonlib.File.from_built_file(self.subdir, output) + return mesonlib.File.from_built_file(output_subdir, output) def extract_incdirs(self, kwargs, key: str = 'include_directories') -> T.List[build.IncludeDirs]: prospectives = extract_as_list(kwargs, key) @@ -2862,28 +2907,28 @@ def func_add_test_setup(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs @typed_pos_args('add_global_arguments', varargs=str) @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_global_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - self._add_global_arguments(node, self.build.global_args[kwargs['native']], args[0], kwargs) + self._add_global_arguments(node, self.build.global_args[self.kwarg_options_to_machine(kwargs)], args[0], kwargs) @typed_pos_args('add_global_link_arguments', varargs=str) @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_global_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - self._add_global_arguments(node, self.build.global_link_args[kwargs['native']], args[0], kwargs) + self._add_global_arguments(node, self.build.global_link_args[self.kwarg_options_to_machine(kwargs)], args[0], kwargs) @typed_pos_args('add_project_arguments', varargs=str) @typed_kwargs('add_project_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - self._add_project_arguments(node, self.build.projects_args[kwargs['native']], args[0], kwargs) + self._add_project_arguments(node, self.build.projects_args[self.kwarg_options_to_machine(kwargs)], args[0], kwargs) @typed_pos_args('add_project_link_arguments', varargs=str) @typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW) def func_add_project_link_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - self._add_project_arguments(node, self.build.projects_link_args[kwargs['native']], args[0], kwargs) + self._add_project_arguments(node, self.build.projects_link_args[self.kwarg_options_to_machine(kwargs)], args[0], kwargs) @FeatureNew('add_project_dependencies', '0.63.0') @typed_pos_args('add_project_dependencies', varargs=dependencies.Dependency) @typed_kwargs('add_project_dependencies', NATIVE_KW, LANGUAGE_KW) def func_add_project_dependencies(self, node: mparser.FunctionNode, args: T.Tuple[T.List[dependencies.Dependency]], kwargs: 'kwtypes.FuncAddProjectArgs') -> None: - for_machine = kwargs['native'] + for_machine = self.kwarg_options_to_machine(kwargs) for lang in kwargs['language']: if lang not in self.compilers[for_machine]: raise InvalidCode(f'add_project_dependencies() called before add_language() for language "{lang}"') @@ -3289,7 +3334,9 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs targetclass: T.Type[T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]] ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedModule, build.SharedLibrary, build.Jar]: name, sources = args - for_machine = kwargs['native'] + for_machine = self.kwarg_options_to_machine(kwargs) + if self.environment.is_native_clone: + kwargs['install'] = False if kwargs.get('rust_crate_type') == 'proc-macro': # Silently force to native because that's the only sensible value # and rust_crate_type is deprecated any way. @@ -3408,6 +3455,11 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs self.project_args_frozen = True return target + def kwarg_options_to_machine(self, kwargs: kwtypes.FuncWithNativeArg) -> MachineChoice: + if self.environment.is_native_clone: + return MachineChoice.BUILD + return kwargs['native'] + def kwarg_strings_to_includedirs(self, kwargs: kwtypes._BuildTarget) -> None: if kwargs['d_import_dirs']: items = kwargs['d_import_dirs'] diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 17f7876a04d0..a6eb1030ca70 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -18,6 +18,11 @@ from ..programs import ExternalProgram from .type_checking import PkgConfigDefineType, SourcesVarargsType +class FuncWithNativeArg(TypedDict): + + native: MachineChoice + + class FuncAddProjectArgs(TypedDict): """Keyword Arguments for the add_*_arguments family of arguments. diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 4d1f427da210..430d734c0eb3 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -220,7 +220,7 @@ def current_build_dir_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwarg sub = self.interpreter.subdir if sub == '': return src - return os.path.join(src, sub) + return os.path.join(src, self.interpreter.environment.build_output_rpath(sub)) @noPosargs @noKwargs @@ -384,7 +384,7 @@ def _override_dependency_impl(self, name: str, dep: dependencies.Dependency, kwa else: nkwargs['static'] = static identifier = dependencies.get_dep_identifier(name, nkwargs) - for_machine = kwargs['native'] + for_machine = self.interpreter.kwarg_options_to_machine(kwargs) override = self.build.dependency_overrides[for_machine].get(identifier) if override: if permissive: diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 952d6f7b1588..4d7e0b52de3c 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -2191,7 +2191,7 @@ def _classify_argument(key: 'OptionKey') -> OptionType: assert key.machine is MachineChoice.HOST, str(key) return OptionType.BACKEND else: - assert key.machine is MachineChoice.HOST, str(key) + assert key.subproject or key.machine is MachineChoice.HOST, str(key) return OptionType.PROJECT