From 86fb916d3368ee7e542608b74cb43a3625f018ee Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 10 Apr 2021 02:24:36 +0300 Subject: Xcode: Fix source generation. --- mesonbuild/backend/backends.py | 14 ++++++- mesonbuild/backend/xcodebackend.py | 79 ++++++++++++++++++++++++++++++++++++-- mesonbuild/build.py | 7 ++++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 0a3875c..f595e09 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -224,6 +224,8 @@ class Backend: self.source_dir = self.environment.get_source_dir() self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) + self.src_to_build = mesonlib.relpath(self.environment.get_build_dir(), + self.environment.get_source_dir()) def generate(self) -> None: raise RuntimeError('generate is not implemented in {}'.format(type(self).__name__)) @@ -1133,12 +1135,22 @@ class Backend: deps.append(os.path.join(self.build_to_src, target.subdir, i)) return deps + def get_custom_target_output_dir(self, target): + # The XCode backend is special. A target foo/bar does + # not go to ${BUILDDIR}/foo/bar but instead to + # ${BUILDDIR}/${BUILDTYPE}/foo/bar. + # Currently we set the include dir to be the former, + # and not the latter. Thus we need this extra customisation + # point. If in the future we make include dirs et al match + # ${BUILDDIR}/${BUILDTYPE} instead, this becomes unnecessary. + return self.get_target_dir(target) + def eval_custom_target_command(self, target, absolute_outputs=False): # We want the outputs to be absolute only when using the VS backend # XXX: Maybe allow the vs backend to use relative paths too? source_root = self.build_to_src build_root = '.' - outdir = self.get_target_dir(target) + outdir = self.get_custom_target_output_dir(target) if absolute_outputs: source_root = self.environment.get_source_dir() build_root = self.environment.get_build_dir() diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index d653a57..2008cb4 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -213,6 +213,11 @@ class XCodeBackend(backends.Backend): os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True) return dirname + def get_custom_target_output_dir(self, target): + dirname = target.get_subdir() + os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True) + return dirname + def target_to_build_root(self, target): if self.get_target_dir(target) == '': return '' @@ -235,6 +240,7 @@ class XCodeBackend(backends.Backend): self.serialize_tests() # Cache the result as the method rebuilds the array every time it is called. self.build_targets = self.build.get_build_targets() + self.custom_targets = self.build.get_custom_targets() self.generate_filemap() self.generate_buildstylemap() self.generate_build_phase_map() @@ -245,6 +251,7 @@ class XCodeBackend(backends.Backend): self.generate_test_configurations_map() self.generate_native_target_map() self.generate_native_frameworks_map() + self.generate_custom_target_map() self.generate_source_phase_map() self.generate_target_dependency_map() self.generate_pbxdep_map() @@ -358,6 +365,19 @@ class XCodeBackend(backends.Backend): for t in self.build_targets: self.native_targets[t] = self.gen_id() + def generate_custom_target_map(self): + self.shell_targets = {} + self.custom_target_output_buildfile = {} + self.custom_target_output_fileref = {} + for tname, t in self.custom_targets.items(): + self.shell_targets[tname] = self.gen_id() + if not isinstance(t, build.CustomTarget): + continue + (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) + for o in ofilenames: + self.custom_target_output_buildfile[o] = self.gen_id() + self.custom_target_output_fileref[o] = self.gen_id() + def generate_native_frameworks_map(self): self.native_frameworks = {} self.native_frameworks_fileref = {} @@ -463,9 +483,6 @@ class XCodeBackend(backends.Backend): compiler_args = '' sdict.add_item('isa', 'PBXBuildFile') sdict.add_item('fileRef', fileref, fullpath) - settingdict = PbxDict() - settingdict.add_item('COMPILER_FLAGS', '"' + compiler_args + '"') - sdict.add_item('settings', settingdict) objects_dict.add_item(idval, sdict) for o in t.objects: @@ -485,6 +502,17 @@ class XCodeBackend(backends.Backend): o_dict.add_item('isa', 'PBXBuildFile') o_dict.add_item('fileRef', fileref, fullpath2) + # Custom targets are shell build phases in Xcode terminology. + for tname, t in self.custom_targets.items(): + if not isinstance(t, build.CustomTarget): + continue + (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) + for o in ofilenames: + custom_dict = PbxDict() + 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]) + 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. for name, idval in self.buildstylemap.items(): @@ -582,6 +610,21 @@ class XCodeBackend(backends.Backend): target_dict.add_item('refType', reftype) target_dict.add_item('sourceTree', 'BUILT_PRODUCTS_DIR') + for tname, t in self.custom_targets.items(): + if not isinstance(t, build.CustomTarget): + continue + (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) + for o in ofilenames: + custom_dict = PbxDict() + typestr = self.get_xcodetype(o) + custom_dict.add_item('isa', 'PBXFileReference') + custom_dict.add_item('explicitFileType', '"' + typestr + '"') + custom_dict.add_item('name', o) + custom_dict.add_item('path', os.path.join(self.src_to_build, o)) + custom_dict.add_item('refType', 0) + custom_dict.add_item('sourceTree', 'SOURCE_ROOT') + objects_dict.add_item(self.custom_target_output_fileref[o], custom_dict) + def generate_pbx_frameworks_buildphase(self, objects_dict): for t in self.build_targets.values(): bt_dict = PbxDict() @@ -702,6 +745,9 @@ class XCodeBackend(backends.Backend): ntarget_dict.add_item('buildConfigurationList', self.buildconflistmap[tname], f'Build configuration list for PBXNativeTarget "{tname}"') buildphases_array = PbxArray() ntarget_dict.add_item('buildPhases', buildphases_array) + for g in t.generated: + if isinstance(g, build.CustomTarget): + buildphases_array.add_item(self.shell_targets[g.get_id()], f'/* {g.name} */') for bpname, bpval in t.buildphasemap.items(): buildphases_array.add_item(bpval, f'{bpname} yyy') ntarget_dict.add_item('buildRules', PbxArray()) @@ -770,6 +816,28 @@ 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) + # Custom targets are shell build phases in Xcode terminology. + for tname, t in self.custom_targets.items(): + if not isinstance(t, build.CustomTarget): + continue + (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) + custom_dict = PbxDict() + objects_dict.add_item(self.shell_targets[tname], custom_dict, f'/* Custom target {tname} */') + custom_dict.add_item('isa', 'PBXShellScriptBuildPhase') + custom_dict.add_item('buildActionMask', 2147483647) + custom_dict.add_item('files', PbxArray()) + custom_dict.add_item('inputPaths', PbxArray()) + outarray = PbxArray() + custom_dict.add_item('name', '"Generate {}."'.format(ofilenames[0])) + custom_dict.add_item('outputPaths', outarray) + for o in ofilenames: + outarray.add_item(os.path.join(self.environment.get_build_dir(), o)) + custom_dict.add_item('runOnlyForDeploymentPostprocessing', 0) + custom_dict.add_item('shellPath', '/bin/sh') + workdir = self.environment.get_build_dir() + cmdstr = ' '.join([f'\\"{x}\\"' for x in cmd]) + custom_dict.add_item('shellScript', f'"cd {workdir}; {cmdstr}"') + custom_dict.add_item('showEnvVarsInLog', 0) def generate_pbx_sources_build_phase(self, objects_dict): for name in self.source_phase.keys(): @@ -784,6 +852,11 @@ class XCodeBackend(backends.Backend): s = os.path.join(s.subdir, s.fname) if not self.environment.is_header(s): file_arr.add_item(self.buildfile_ids[(name, s)], os.path.join(self.environment.get_source_dir(), s)) + for tname, t in self.custom_targets.items(): + (srcs, ofilenames, cmd) = self.eval_custom_target_command(t) + for o in ofilenames: + file_arr.add_item(self.custom_target_output_buildfile[o], + os.path.join(self.environment.get_build_dir(), o)) phase_dict.add_item('runOnlyForDeploymentPostprocessing', 0) def generate_pbx_target_dependency(self, objects_dict): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 533375e..ab2866e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -239,6 +239,13 @@ class Build: build_targets[name] = t return build_targets + def get_custom_targets(self): + custom_targets = OrderedDict() + for name, t in self.targets.items(): + if isinstance(t, CustomTarget): + custom_targets[name] = t + return custom_targets + def copy(self): other = Build(self.environment) for k, v in self.__dict__.items(): -- cgit v1.1