Repository: mesonbuild/meson

Issue:
MSVC backend crashes when `c_pch` or `cpp_pch` is not an array
https://github.com/mesonbuild/meson/blob/5626df46453e73b63827c4542aae36443fbd928b/mesonbuild/backend/vs2010backend.py#L864

```
Traceback (most recent call last):
  File "mesonbuild\mesonmain.py", line 352, in run
  File "mesonbuild\mesonmain.py", line 147, in generate
  File "mesonbuild\mesonmain.py", line 197, in _generate
  File "mesonbuild\backend\vs2010backend.py", line 161, in generate
  File "mesonbuild\backend\vs2010backend.py", line 311, in generate_projects
  File "mesonbuild\backend\vs2010backend.py", line 864, in gen_vcxproj
IndexError: list index out of range
```

The PCH example from the manual is sufficient to reproduce this issue: http://mesonbuild.com/Precompiled-headers.html

```meson
project('issue2833', 'c')
sourcelist = files('main.c')
executable('myexe', sources : sourcelist, c_pch : 'pch/myexe_pch.h')
```

Build with `meson --backend vs2017 builddir/`.

---

_Side note:_ The Ninja backend is slightly more helpful. It tells you that MSVC expects `c_pch` and `cpp_pch` to be an array.

```
Traceback (most recent call last):
  File "mesonbuild\mesonmain.py", line 352, in run
  File "mesonbuild\mesonmain.py", line 147, in generate
  File "mesonbuild\mesonmain.py", line 197, in _generate
  File "mesonbuild\backend\ninjabackend.py", line 210, in generate
  File "mesonbuild\backend\ninjabackend.py", line 362, in generate_target
  File "mesonbuild\backend\ninjabackend.py", line 2283, in generate_pch
  File "mesonbuild\backend\ninjabackend.py", line 2244, in generate_msvc_pch_command
RuntimeError: MSVC requires one header and one source to produce precompiled headers.
```


Discussion:

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2254,7 +2254,7 @@ def get_fortran_orderdeps(self, target, compiler):
 
     def generate_msvc_pch_command(self, target, compiler, pch):
         if len(pch) != 2:
-            raise RuntimeError('MSVC requires one header and one source to produce precompiled headers.')
+            raise MesonException('MSVC requires one header and one source to produce precompiled headers.')
         header = pch[0]
         source = pch[1]
         pchname = compiler.get_pch_name(header)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -861,7 +861,14 @@ def gen_vcxproj(self, target, ofname, guid):
             if not pch:
                 continue
             pch_node.text = 'Use'
-            pch_sources[lang] = [pch[0], pch[1], lang]
+            if compiler.id == 'msvc':
+                if len(pch) != 2:
+                    raise MesonException('MSVC requires one header and one source to produce precompiled headers.')
+                pch_sources[lang] = [pch[0], pch[1], lang]
+            else:
+                # I don't know whether its relevant but let's handle other compilers
+                # used with a vs backend
+                pch_sources[lang] = [pch[0], None, lang]
         if len(pch_sources) == 1:
             # If there is only 1 language with precompiled headers, we can use it for the entire project, which
             # is cleaner than specifying it for each source file.
@@ -1016,19 +1023,20 @@ def gen_vcxproj(self, target, ofname, guid):
                 self.add_include_dirs(lang, inc_cl, file_inc_dirs)
             for lang in pch_sources:
                 header, impl, suffix = pch_sources[lang]
-                relpath = os.path.join(proj_to_src_dir, impl)
-                inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
-                pch = ET.SubElement(inc_cl, 'PrecompiledHeader')
-                pch.text = 'Create'
-                pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile')
-                pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % suffix
-                pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile')
-                # MSBuild searches for the header relative from the implementation, so we have to use
-                # just the file name instead of the relative path to the file.
-                pch_file.text = os.path.split(header)[1]
-                self.add_additional_options(lang, inc_cl, file_args)
-                self.add_preprocessor_defines(lang, inc_cl, file_defines)
-                self.add_include_dirs(lang, inc_cl, file_inc_dirs)
+                if impl:
+                    relpath = os.path.join(proj_to_src_dir, impl)
+                    inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
+                    pch = ET.SubElement(inc_cl, 'PrecompiledHeader')
+                    pch.text = 'Create'
+                    pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile')
+                    pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % suffix
+                    pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile')
+                    # MSBuild searches for the header relative from the implementation, so we have to use
+                    # just the file name instead of the relative path to the file.
+                    pch_file.text = os.path.split(header)[1]
+                    self.add_additional_options(lang, inc_cl, file_args)
+                    self.add_preprocessor_defines(lang, inc_cl, file_defines)
+                    self.add_include_dirs(lang, inc_cl, file_inc_dirs)
 
         if self.has_objects(objects, additional_objects, gen_objs):
             inc_objs = ET.SubElement(root, 'ItemGroup')


Test patch: