Skip to content

Commit

Permalink
Xcode: regenerato project file when build conf changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpakkane committed Apr 19, 2021
1 parent 045893b commit 6f76fce
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 25 deletions.
16 changes: 16 additions & 0 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
# 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
Expand Down Expand Up @@ -1007,6 +1013,16 @@ def get_regen_filelist(self):
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
Expand Down
17 changes: 0 additions & 17 deletions mesonbuild/backend/vs2010backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import copy
import os
import pickle
import xml.dom.minidom
import xml.etree.ElementTree as ET
import uuid
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -206,16 +199,6 @@ def touch_regen_timestamp(build_dir: str) -> None:
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

Expand Down
65 changes: 58 additions & 7 deletions mesonbuild/backend/xcodebackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter
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
Expand All @@ -203,9 +207,11 @@ def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Inter
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]
Expand Down Expand Up @@ -308,6 +314,7 @@ def generate(self):
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())
Expand Down Expand Up @@ -500,13 +507,25 @@ def generate_pbx_aggregate_target(self, objects_dict):
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):
Expand Down Expand Up @@ -949,6 +968,7 @@ def generate_pbx_native_target(self, objects_dict):
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.
Expand All @@ -971,7 +991,6 @@ def generate_pbx_native_target(self, objects_dict):

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)
Expand Down Expand Up @@ -1007,12 +1026,19 @@ def generate_pbx_project(self, objects_dict):
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')
Expand All @@ -1026,8 +1052,21 @@ def generate_pbx_shell_build_phase(self, objects_dict):
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.
Expand Down Expand Up @@ -1160,6 +1199,7 @@ def generate_pbx_target_dependency(self, objects_dict):
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]))
Expand Down Expand Up @@ -1449,6 +1489,17 @@ def generate_xc_configurationList(self, objects_dict):
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]
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/scripts/regen_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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.
Expand Down

0 comments on commit 6f76fce

Please sign in to comment.