Xcode: fix generators that take custom targets as inputs.

pull/7079/head
Jussi Pakkanen 4 years ago
parent e422e9c634
commit 68dda2a241
  1. 201
      mesonbuild/backend/xcodebackend.py
  2. 10
      mesonbuild/build.py

@ -396,25 +396,36 @@ class XCodeBackend(backends.Backend):
for genlist in t.generated:
if not isinstance(genlist, build.GeneratedList):
continue
k = (tname, generator_id)
assert(k not in self.shell_targets)
self.shell_targets[k] = self.gen_id()
ofile_abs = []
for i in genlist.get_inputs():
for o_base in genlist.get_outputs_for(i):
o = os.path.join(self.get_target_private_dir(t), o_base)
ofile_abs.append(os.path.join(self.environment.get_build_dir(), o))
assert(k not in self.generator_outputs)
self.generator_outputs[k] = ofile_abs
buildfile_ids = []
fileref_ids = []
for i in range(len(ofile_abs)):
buildfile_ids.append(self.gen_id())
fileref_ids.append(self.gen_id())
self.generator_buildfile_ids[k] = buildfile_ids
self.generator_fileref_ids[k] = fileref_ids
self.gen_single_target_map(genlist, tname, t, generator_id)
generator_id += 1
# FIXME add outputs.
for tname, t in self.custom_targets.items():
generator_id = 0
for genlist in t.sources:
if not isinstance(genlist, build.GeneratedList):
continue
self.gen_single_target_map(genlist, tname, t, generator_id)
generator_id += 1
def gen_single_target_map(self, genlist, tname, t, generator_id):
k = (tname, generator_id)
assert(k not in self.shell_targets)
self.shell_targets[k] = self.gen_id()
ofile_abs = []
for i in genlist.get_inputs():
for o_base in genlist.get_outputs_for(i):
o = os.path.join(self.get_target_private_dir(t), o_base)
ofile_abs.append(os.path.join(self.environment.get_build_dir(), o))
assert(k not in self.generator_outputs)
self.generator_outputs[k] = ofile_abs
buildfile_ids = []
fileref_ids = []
for i in range(len(ofile_abs)):
buildfile_ids.append(self.gen_id())
fileref_ids.append(self.gen_id())
self.generator_buildfile_ids[k] = buildfile_ids
self.generator_fileref_ids[k] = fileref_ids
def generate_native_frameworks_map(self):
self.native_frameworks = {}
@ -494,7 +505,18 @@ class XCodeBackend(backends.Backend):
for tname, t in self.build.get_custom_targets().items():
ct_id = self.gen_id()
self.custom_aggregate_targets[tname] = ct_id
aggregated_targets.append((ct_id, tname, self.buildconflistmap[tname], [self.shell_targets[tname]], []))
build_phases = []
dependencies = []
generator_id = 0
for s in t.sources:
if not isinstance(s, build.GeneratedList):
continue
build_phases.append(self.shell_targets[(tname, generator_id)])
for d in s.depends:
dependencies.append(self.pbx_custom_dep_map[d.get_id()])
generator_id += 1
build_phases.append(self.shell_targets[tname])
aggregated_targets.append((ct_id, tname, self.buildconflistmap[tname], build_phases, dependencies))
# Sort objects by ID before writing
sorted_aggregated_targets = sorted(aggregated_targets, key=operator.itemgetter(0))
@ -573,17 +595,7 @@ class XCodeBackend(backends.Backend):
for g in t.generated:
if not isinstance(g, build.GeneratedList):
continue
file_ids = self.generator_buildfile_ids[(tname, generator_id)]
ref_ids = self.generator_fileref_ids[tname, generator_id]
assert(len(ref_ids) == len(file_ids))
for i in range(len(file_ids)):
file_o = file_ids[i]
ref_id = ref_ids[i]
odict = PbxDict()
objects_dict.add_item(file_o, odict)
odict.add_item('isa', 'PBXBuildFile')
odict.add_item('fileRef', ref_id)
self.create_generator_shellphase(objects_dict, tname, generator_id)
generator_id += 1
# Custom targets are shell build phases in Xcode terminology.
@ -596,6 +608,24 @@ class XCodeBackend(backends.Backend):
objects_dict.add_item(self.custom_target_output_buildfile[o], custom_dict, f'/* {o} */')
custom_dict.add_item('isa', 'PBXBuildFile')
custom_dict.add_item('fileRef', self.custom_target_output_fileref[o])
generator_id = 0
for g in t.sources:
if not isinstance(g, build.GeneratedList):
continue
self.create_generator_shellphase(objects_dict, tname, generator_id)
generator_id += 1
def create_generator_shellphase(self, objects_dict, tname, generator_id):
file_ids = self.generator_buildfile_ids[(tname, generator_id)]
ref_ids = self.generator_fileref_ids[(tname, generator_id)]
assert(len(ref_ids) == len(file_ids))
for i in range(len(file_ids)):
file_o = file_ids[i]
ref_id = ref_ids[i]
odict = PbxDict()
objects_dict.add_item(file_o, odict)
odict.add_item('isa', 'PBXBuildFile')
odict.add_item('fileRef', ref_id)
def generate_pbx_build_style(self, objects_dict):
# FIXME: Xcode 9 and later does not uses PBXBuildStyle and it gets removed. Maybe we can remove this part.
@ -1033,59 +1063,68 @@ class XCodeBackend(backends.Backend):
generator_id = 0
for genlist in t.generated:
if isinstance(genlist, build.GeneratedList):
generator = genlist.get_generator()
exe = generator.get_exe()
exe_arr = self.build_target_to_cmd_array(exe)
workdir = self.environment.get_build_dir()
gen_dict = PbxDict()
objects_dict.add_item(self.shell_targets[(tname, generator_id)], gen_dict, '"Generator {}/{}"'.format(generator_id, tname))
infilelist = genlist.get_inputs()
outfilelist = genlist.get_outputs()
gen_dict.add_item('isa', 'PBXShellScriptBuildPhase')
gen_dict.add_item('buildActionMask', 2147483647)
gen_dict.add_item('files', PbxArray())
gen_dict.add_item('inputPaths', PbxArray())
gen_dict.add_item('name', '"Generator {}/{}"'.format(generator_id, tname))
commands = [["cd", workdir]] # Array of arrays, each one a single command, will get concatenated below.
k = (tname, generator_id)
ofile_abs = self.generator_outputs[k]
outarray = PbxArray()
gen_dict.add_item('outputPaths', outarray)
for of in ofile_abs:
outarray.add_item(of)
for i in infilelist:
# This might be needed to be added to inputPaths. It's not done yet as it is
# unclear whether it is necessary, what actually happens when it is defined
# and currently the build works without it.
#infile_abs = i.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
infilename = i.rel_to_builddir(self.build_to_src)
base_args = generator.get_arglist(infilename)
for o_base in genlist.get_outputs_for(i):
o = os.path.join(self.get_target_private_dir(t), o_base)
args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', o).replace('@BUILD_DIR@', self.get_target_private_dir(t)) for x in base_args]
args = self.replace_outputs(args, self.get_target_private_dir(t), outfilelist)
args = self.replace_extra_args(args, genlist)
if generator.capture:
# When capturing, stdout is the output. Forward it with the shell.
full_command = ['('] + exe_arr + args + ['>', o, ')']
else:
full_command = exe_arr + args
commands.append(full_command)
gen_dict.add_item('runOnlyForDeploymentPostprocessing', 0)
gen_dict.add_item('shellPath', '/bin/sh')
quoted_cmds = []
for cmnd in commands:
q = []
for c in cmnd:
if ' ' in c:
q.append(f'\\"{c}\\"')
else:
q.append(c)
quoted_cmds.append(' '.join(q))
cmdstr = '"' + ' && '.join(quoted_cmds) + '"'
gen_dict.add_item('shellScript', cmdstr)
gen_dict.add_item('showEnvVarsInLog', 0)
self.generate_single_generator_phase(tname, t, genlist, generator_id, objects_dict)
generator_id += 1
for tname, t in self.custom_targets.items():
generator_id = 0
for genlist in t.sources:
if isinstance(genlist, build.GeneratedList):
self.generate_single_generator_phase(tname, t, genlist, generator_id, objects_dict)
generator_id += 1
def generate_single_generator_phase(self, tname, t, genlist, generator_id, objects_dict):
generator = genlist.get_generator()
exe = generator.get_exe()
exe_arr = self.build_target_to_cmd_array(exe)
workdir = self.environment.get_build_dir()
gen_dict = PbxDict()
objects_dict.add_item(self.shell_targets[(tname, generator_id)], gen_dict, '"Generator {}/{}"'.format(generator_id, tname))
infilelist = genlist.get_inputs()
outfilelist = genlist.get_outputs()
gen_dict.add_item('isa', 'PBXShellScriptBuildPhase')
gen_dict.add_item('buildActionMask', 2147483647)
gen_dict.add_item('files', PbxArray())
gen_dict.add_item('inputPaths', PbxArray())
gen_dict.add_item('name', '"Generator {}/{}"'.format(generator_id, tname))
commands = [["cd", workdir]] # Array of arrays, each one a single command, will get concatenated below.
k = (tname, generator_id)
ofile_abs = self.generator_outputs[k]
outarray = PbxArray()
gen_dict.add_item('outputPaths', outarray)
for of in ofile_abs:
outarray.add_item(of)
for i in infilelist:
# This might be needed to be added to inputPaths. It's not done yet as it is
# unclear whether it is necessary, what actually happens when it is defined
# and currently the build works without it.
#infile_abs = i.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
infilename = i.rel_to_builddir(self.build_to_src)
base_args = generator.get_arglist(infilename)
for o_base in genlist.get_outputs_for(i):
o = os.path.join(self.get_target_private_dir(t), o_base)
args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', o).replace('@BUILD_DIR@', self.get_target_private_dir(t)) for x in base_args]
args = self.replace_outputs(args, self.get_target_private_dir(t), outfilelist)
args = self.replace_extra_args(args, genlist)
if generator.capture:
# When capturing, stdout is the output. Forward it with the shell.
full_command = ['('] + exe_arr + args + ['>', o, ')']
else:
full_command = exe_arr + args
commands.append(full_command)
gen_dict.add_item('runOnlyForDeploymentPostprocessing', 0)
gen_dict.add_item('shellPath', '/bin/sh')
quoted_cmds = []
for cmnd in commands:
q = []
for c in cmnd:
if ' ' in c:
q.append(f'\\"{c}\\"')
else:
q.append(c)
quoted_cmds.append(' '.join(q))
cmdstr = '"' + ' && '.join(quoted_cmds) + '"'
gen_dict.add_item('shellScript', cmdstr)
gen_dict.add_item('showEnvVarsInLog', 0)
def generate_pbx_sources_build_phase(self, objects_dict):

@ -1580,10 +1580,15 @@ class Generator:
def process_files(self, name, files, state, preserve_path_from=None, extra_args=None):
new = False
output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [])
#XXX
for e in unholder(files):
fs = [e]
if isinstance(e, CustomTarget):
output.depends.add(e)
if isinstance(e, CustomTargetIndex):
output.depends.add(e.target)
if isinstance(e, (CustomTarget, CustomTargetIndex, GeneratedList)):
self.depends.append(e)
self.depends.append(e) # BUG: this should go in the GeneratedList object, not this object.
fs = []
for f in e.get_outputs():
fs.append(File.from_built_file(state.subdir, f))
@ -1610,6 +1615,7 @@ class GeneratedList:
def __init__(self, generator, subdir, preserve_path_from=None, extra_args=None):
self.generator = unholder(generator)
self.name = self.generator.exe
self.depends = set() # Things this target depends on (because e.g. a custom target was used as input)
self.subdir = subdir
self.infilelist = []
self.outfilelist = []
@ -2545,7 +2551,7 @@ class Jar(BuildTarget):
class CustomTargetIndex:
"""A special opaque object returned by indexing a CustomTarget. This object
exists in meson, but acts as a proxy in the backends, making targets depend
exists in Meson, but acts as a proxy in the backends, making targets depend
on the CustomTarget it's derived from, but only adding one source file to
the sources.
"""

Loading…
Cancel
Save