Xcode: Fix source generation.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 0a3875c..1f042f0 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -224,6 +224,8 @@
         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 @@
                     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 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..096ba3c 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -213,6 +213,11 @@
         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 @@
         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 @@
         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,20 @@
         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:
+                custom_dict = PbxDict()
+                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 +484,6 @@
                 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 +503,17 @@
                 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 +611,21 @@
             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 +746,9 @@
             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 +817,29 @@
         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)
+            outputdir = self.get_custom_target_output_dir(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 +854,11 @@
                 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 @@
                 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():