Repository: mesonbuild/meson

Issue:
Custom manifest does not work with VS backend
**Describe the bug**
I use `.manifest` files with the `/MANIFEST:NO` option in my projects.
They worked fine with old versions of meson (e.g. 1.3.2)
But I got errors with meson 1.4.0.

```
***.exe.intermediate.manifest : general error c1010070: Failed to load and parse the manifest. The system cannot find the file specified.
```

I can see `/ManifestFile:"***.exe.intermediate.manifest"` in linker options but I don't think there is the file because it uses my custom `.manifest` file.

**To Reproduce**
Build [libui-ng](https://github.com/libui-ng/libui-ng) with VS backend.
```
git clone https://github.com/libui-ng/libui-ng.git
cd libui-ng
meson setup builddir --backend=vs
meson compile -C builddir --verbose
```
Or see my failed workflow.
https://github.com/matyalatte/libui-ng/actions/runs/8558688815/job/23453770280

**Expected behavior**
Pass the failed workflow
https://github.com/matyalatte/libui-ng/actions/runs/8558688815/job/23453770280

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)?
native build

* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.)
Windows 10

* what Python version are you using e.g. 3.8.0
Idk. I installed meson with the released msi. (meson-1.4.0-64.msi)

* what `meson --version`
1.4.0
* what `ninja --version` if it's a Ninja build
not ninja build



Discussion:
https://github.com/mesonbuild/meson/pull/12472 could be the cause. and it should be reverted.
Visual Studio creates a default manifest even if `EmbedManifest` is false. (I can see `***.exe.manifest` in the build directory even when using a custom manifest with meson 1.4.0.)
The only way I know to disable the manifest creation is to use `/MANIFEST:NO`. But it conflicts with the `/ManifestFile:` option. It means `EmbedManifest` should be true.

@LBPHacker
Sorry for the ping but I think you should see this comment.
I doubt that you disabled the manifest creation in your project.
And I think you can use `/MANIFEST:NO` to use your custom manifest with meson 1.3.2.

Resolving patch:
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -616,7 +616,8 @@ def create_basic_project(self, target_name, *,
                              guid,
                              conftype='Utility',
                              target_ext=None,
-                             target_platform=None) -> T.Tuple[ET.Element, ET.Element]:
+                             target_platform=None,
+                             gen_manifest=True) -> T.Tuple[ET.Element, ET.Element]:
         root = ET.Element('Project', {'DefaultTargets': "Build",
                                       'ToolsVersion': '4.0',
                                       'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'})
@@ -688,13 +689,16 @@ def create_basic_project(self, target_name, *,
                 ET.SubElement(direlem, 'TargetExt').text = target_ext
 
             ET.SubElement(direlem, 'EmbedManifest').text = 'false'
+            if not gen_manifest:
+                ET.SubElement(direlem, 'GenerateManifest').text = 'false'
 
         return (root, type_config)
 
     def gen_run_target_vcxproj(self, target: build.RunTarget, ofname: str, guid: str) -> None:
         (root, type_config) = self.create_basic_project(target.name,
                                                         temp_dir=target.get_id(),
-                                                        guid=guid)
+                                                        guid=guid,
+                                                        gen_manifest=self.get_gen_manifest(target))
         depend_files = self.get_target_depend_files(target)
 
         if not target.command:
@@ -729,7 +733,8 @@ def gen_custom_target_vcxproj(self, target: build.CustomTarget, ofname: str, gui
         (root, type_config) = self.create_basic_project(target.name,
                                                         temp_dir=target.get_id(),
                                                         guid=guid,
-                                                        target_platform=platform)
+                                                        target_platform=platform,
+                                                        gen_manifest=self.get_gen_manifest(target))
         # We need to always use absolute paths because our invocation is always
         # from the target dir, not the build root.
         target.absolute_paths = True
@@ -769,7 +774,8 @@ def gen_compile_target_vcxproj(self, target: build.CompileTarget, ofname: str, g
         (root, type_config) = self.create_basic_project(target.name,
                                                         temp_dir=target.get_id(),
                                                         guid=guid,
-                                                        target_platform=platform)
+                                                        target_platform=platform,
+                                                        gen_manifest=self.get_gen_manifest(target))
         ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
         target.generated = [self.compile_target_to_generator(target)]
         target.sources = []
@@ -1601,7 +1607,8 @@ def gen_vcxproj(self, target: build.BuildTarget, ofname: str, guid: str, vslite_
                                                         guid=guid,
                                                         conftype=conftype,
                                                         target_ext=tfilename[1],
-                                                        target_platform=platform)
+                                                        target_platform=platform,
+                                                        gen_manifest=self.get_gen_manifest(target))
 
         generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(
             target, root)
@@ -2080,3 +2087,25 @@ def add_regen_dependency(self, root: ET.Element) -> None:
 
     def generate_lang_standard_info(self, file_args: T.Dict[str, CompilerArgs], clconf: ET.Element) -> None:
         pass
+
+    # Returns if a target generates a manifest or not.
+    def get_gen_manifest(self, target):
+        if not isinstance(target, build.BuildTarget):
+            return True
+
+        compiler = self._get_cl_compiler(target)
+        link_args = compiler.compiler_args()
+        if not isinstance(target, build.StaticLibrary):
+            link_args += self.build.get_project_link_args(compiler, target.subproject, target.for_machine)
+            link_args += self.build.get_global_link_args(compiler, target.for_machine)
+            link_args += self.environment.coredata.get_external_link_args(
+                target.for_machine, compiler.get_language())
+            link_args += target.link_args
+
+        for arg in reversed(link_args):
+            arg = arg.upper()
+            if arg == '/MANIFEST:NO':
+                return False
+            if arg == '/MANIFEST' or arg.startswith('/MANIFEST:'):
+                break
+        return True


Test patch: