diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 90d7b79d0..04f3d23cf 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -49,6 +49,12 @@ if T.TYPE_CHECKING: # Assembly files cannot be unitified and neither can LLVM IR files LANGS_CANT_UNITY = ('d', 'fortran', 'vala') +class RegenInfo: + def __init__(self, source_dir, build_dir, depfiles): + self.source_dir = source_dir + self.build_dir = build_dir + self.depfiles = depfiles + class TestProtocol(enum.Enum): EXITCODE = 0 @@ -1007,6 +1013,16 @@ class Backend: self.check_clock_skew(deps) return deps + def generate_regen_info(self): + deps = self.get_regen_filelist() + regeninfo = RegenInfo(self.environment.get_source_dir(), + self.environment.get_build_dir(), + deps) + filename = os.path.join(self.environment.get_scratch_dir(), + 'regeninfo.dump') + with open(filename, 'wb') as f: + pickle.dump(regeninfo, f) + def check_clock_skew(self, file_list): # If a file that leads to reconfiguration has a time # stamp in the future, it will trigger an eternal reconfigure diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ee58114e6..6b5fd2d9c 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -14,7 +14,6 @@ import copy import os -import pickle import xml.dom.minidom import xml.etree.ElementTree as ET import uuid @@ -80,12 +79,6 @@ def split_o_flags_args(args): def generate_guid_from_path(path, path_type): return str(uuid.uuid5(uuid.NAMESPACE_URL, 'meson-vs-' + path_type + ':' + str(path))).upper() -class RegenInfo: - def __init__(self, source_dir, build_dir, depfiles): - self.source_dir = source_dir - self.build_dir = build_dir - self.depfiles = depfiles - class Vs2010Backend(backends.Backend): def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): super().__init__(build, interpreter) @@ -206,16 +199,6 @@ class Vs2010Backend(backends.Backend): with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w'): pass - def generate_regen_info(self): - deps = self.get_regen_filelist() - regeninfo = RegenInfo(self.environment.get_source_dir(), - self.environment.get_build_dir(), - deps) - filename = os.path.join(self.environment.get_scratch_dir(), - 'regeninfo.dump') - with open(filename, 'wb') as f: - pickle.dump(regeninfo, f) - def get_vcvars_command(self): has_arch_values = 'VSCMD_ARG_TGT_ARCH' in os.environ and 'VSCMD_ARG_HOST_ARCH' in os.environ diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 70d1b8ba0..432f2346a 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -189,6 +189,10 @@ class XCodeBackend(backends.Backend): self.test_id = self.gen_id() self.test_command_id = self.gen_id() self.test_buildconf_id = self.gen_id() + self.regen_id = self.gen_id() + self.regen_command_id = self.gen_id() + self.regen_buildconf_id = self.gen_id() + self.regen_dependency_id = self.gen_id() self.top_level_dict = PbxDict() self.generator_outputs = {} # In Xcode files are not accessed via their file names, but rather every one of them @@ -203,9 +207,11 @@ class XCodeBackend(backends.Backend): self.fileref_ids = {} def write_pbxfile(self, top_level_dict, ofilename): - with open(ofilename, 'w') as ofile: + tmpname = ofilename + '.tmp' + with open(tmpname, 'w', encoding='utf-8') as ofile: ofile.write('// !$*UTF8*$!\n') top_level_dict.write(ofile, 0) + os.replace(tmpname, ofilename) def gen_id(self): return str(uuid.uuid4()).upper().replace('-', '')[:24] @@ -308,6 +314,7 @@ class XCodeBackend(backends.Backend): objects_dict.add_comment(PbxComment('End XCConfigurationList section')) self.generate_suffix(self.top_level_dict) self.write_pbxfile(self.top_level_dict, self.proj_file) + self.generate_regen_info() def get_xcodetype(self, fname): xcodetype = XCODETYPEMAP.get(fname.split('.')[-1].lower()) @@ -500,13 +507,25 @@ class XCodeBackend(backends.Backend): target_dependencies = list(map(lambda t: self.pbx_dep_map[t], self.build_targets)) custom_target_dependencies = [self.pbx_custom_dep_map[t] for t in self.custom_targets] aggregated_targets = [] - aggregated_targets.append((self.all_id, 'ALL_BUILD', self.all_buildconf_id, [], target_dependencies + custom_target_dependencies)) - aggregated_targets.append((self.test_id, 'RUN_TESTS', self.test_buildconf_id, [self.test_command_id], [self.build_all_tdep_id])) + aggregated_targets.append((self.all_id, 'ALL_BUILD', + self.all_buildconf_id, + [], + [self.regen_dependency_id] + target_dependencies + custom_target_dependencies)) + aggregated_targets.append((self.test_id, + 'RUN_TESTS', + self.test_buildconf_id, + [self.test_command_id], + [self.regen_dependency_id, self.build_all_tdep_id])) + aggregated_targets.append((self.regen_id, + 'REGENERATE', + self.regen_buildconf_id, + [self.regen_command_id], + [])) for tname, t in self.build.get_custom_targets().items(): ct_id = self.gen_id() self.custom_aggregate_targets[tname] = ct_id build_phases = [] - dependencies = [] + dependencies = [self.regen_dependency_id] generator_id = 0 for s in t.sources: if not isinstance(s, build.GeneratedList): @@ -949,6 +968,7 @@ class XCodeBackend(backends.Backend): ntarget_dict.add_item('buildRules', PbxArray()) dep_array = PbxArray() ntarget_dict.add_item('dependencies', dep_array) + dep_array.add_item(self.regen_dependency_id) # These dependencies only tell Xcode that the deps must be built # before this one. They don't set up linkage or anything # like that. Those are set up in the XCBuildConfiguration. @@ -971,7 +991,6 @@ class XCodeBackend(backends.Backend): generator_id += 1 - ntarget_dict.add_item('name', f'"{tname}"') ntarget_dict.add_item('productName', f'"{tname}"') ntarget_dict.add_item('productReference', self.target_filemap[tname], tname) @@ -1007,12 +1026,19 @@ class XCodeBackend(backends.Backend): project_dict.add_item('targets', targets_arr) targets_arr.add_item(self.all_id, 'ALL_BUILD') targets_arr.add_item(self.test_id, 'RUN_TESTS') + targets_arr.add_item(self.regen_id, 'REGENERATE') for t in self.build_targets: targets_arr.add_item(self.native_targets[t], t) for t in self.custom_targets: targets_arr.add_item(self.custom_aggregate_targets[t], t) def generate_pbx_shell_build_phase(self, objects_dict): + self.generate_test_shell_build_phase(objects_dict) + self.generate_regen_shell_build_phase(objects_dict) + self.generate_custom_target_shell_build_phases(objects_dict) + self.generate_generator_target_shell_build_phases(objects_dict) + + def generate_test_shell_build_phase(self, objects_dict): shell_dict = PbxDict() objects_dict.add_item(self.test_command_id, shell_dict, 'ShellScript') shell_dict.add_item('isa', 'PBXShellScriptBuildPhase') @@ -1026,8 +1052,21 @@ class XCodeBackend(backends.Backend): cmdstr = ' '.join(["'%s'" % i for i in cmd]) shell_dict.add_item('shellScript', f'"{cmdstr}"') shell_dict.add_item('showEnvVarsInLog', 0) - self.generate_custom_target_shell_build_phases(objects_dict) - self.generate_generator_target_shell_build_phases(objects_dict) + + def generate_regen_shell_build_phase(self, objects_dict): + shell_dict = PbxDict() + objects_dict.add_item(self.regen_command_id, shell_dict, 'ShellScript') + shell_dict.add_item('isa', 'PBXShellScriptBuildPhase') + shell_dict.add_item('buildActionMask', 2147483647) + shell_dict.add_item('files', PbxArray()) + shell_dict.add_item('inputPaths', PbxArray()) + shell_dict.add_item('outputPaths', PbxArray()) + shell_dict.add_item('runOnlyForDeploymentPostprocessing', 0) + shell_dict.add_item('shellPath', '/bin/sh') + cmd = mesonlib.get_meson_command() + ['--internal', 'regencheck', os.path.join(self.environment.get_build_dir(), 'meson-private')] + cmdstr = ' '.join(["'%s'" % i for i in cmd]) + shell_dict.add_item('shellScript', f'"{cmdstr}"') + shell_dict.add_item('showEnvVarsInLog', 0) def generate_custom_target_shell_build_phases(self, objects_dict): # Custom targets are shell build phases in Xcode terminology. @@ -1160,6 +1199,7 @@ class XCodeBackend(backends.Backend): all_dict.add_item('isa', 'PBXTargetDependency') all_dict.add_item('target', self.all_id) targets = [] + targets.append((self.regen_dependency_id, self.regen_id, 'REGEN', None)) for t in self.build_targets: idval = self.pbx_dep_map[t] # VERIFY: is this correct? targets.append((idval, self.native_targets[t], t, self.containerproxy_map[t])) @@ -1449,6 +1489,17 @@ class XCodeBackend(backends.Backend): test_dict.add_item('defaultConfigurationIsVisible', 0) test_dict.add_item('defaultConfigurationName', self.buildtype) + # Regen target + regen_dict = PbxDict() + objects_dict.add_item(self.regen_buildconf_id, test_dict, 'Build configuration list for PBXAggregateTarget "REGENERATE"') + regen_dict.add_item('isa', 'XCConfigurationList') + conf_arr = PbxArray() + regen_dict.add_item('buildConfigurations', conf_arr) + for buildtype in self.buildtypes: + conf_arr.add_item(self.test_configurations[buildtype], buildtype) + regen_dict.add_item('defaultConfigurationIsVisible', 0) + regen_dict.add_item('defaultConfigurationName', self.buildtype) + for target_name in self.build_targets: t_dict = PbxDict() listid = self.buildconflistmap[target_name] diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py index 11877831f..c96bdc1e5 100644 --- a/mesonbuild/scripts/regen_checker.py +++ b/mesonbuild/scripts/regen_checker.py @@ -16,7 +16,7 @@ import sys, os import pickle, subprocess import typing as T from ..coredata import CoreData -from ..backend.vs2010backend import RegenInfo +from ..backend.backends import RegenInfo from ..mesonlib import OptionKey # This could also be used for XCode.