We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MSI Installer: Support upgrade
I just attempted to upgrade from meson `0.42.1` to `0.43` and was presented with this screen using the MSI installer

![2017-10-11 14_37_47-windows installer](https://user-images.githubusercontent.com/3422847/31468523-efdaf1d2-ae92-11e7-8c93-04b1978479cf.png)

At the very least, that error message should be a bit more clear.

Besides that - a huge :+1: for having a windows installer!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `msi/createmsi.py`
Content:
```
1 #!/usr/bin/env python3
2 
3 # Copyright 2017 The Meson development team
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import sys, os, subprocess, shutil, uuid
18 from glob import glob
19 import platform
20 import xml.etree.ElementTree as ET
21 
22 sys.path.append(os.getcwd())
23 from mesonbuild import coredata
24 
25 def gen_guid():
26     return str(uuid.uuid4()).upper()
27 
28 class Node:
29     def __init__(self, dirs, files):
30         assert(isinstance(dirs, list))
31         assert(isinstance(files, list))
32         self.dirs = dirs
33         self.files = files
34 
35 class PackageGenerator:
36 
37     def __init__(self):
38         self.product_name = 'Meson Build System'
39         self.manufacturer = 'The Meson Development Team'
40         self.version = coredata.version.replace('dev', '')
41         self.guid = 'DF5B3ECA-4A31-43E3-8CE4-97FC8A97212E'
42         self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2'
43         self.main_xml = 'meson.wxs'
44         self.main_o = 'meson.wixobj'
45         self.bytesize = 32 if '32' in platform.architecture()[0] else 64
46         # rely on the environment variable since python architecture may not be the same as system architecture
47         if 'PROGRAMFILES(X86)' in os.environ:
48             self.bytesize = 64
49         self.final_output = 'meson-%s-%d.msi' % (self.version, self.bytesize)
50         self.staging_dirs = ['dist', 'dist2']
51         if self.bytesize == 64:
52             self.progfile_dir = 'ProgramFiles64Folder'
53             redist_glob = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\*\\MergeModules\\Microsoft_VC141_CRT_x64.msm'
54         else:
55             self.progfile_dir = 'ProgramFilesFolder'
56             redist_glob = 'C:\\Program Files\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\*\\MergeModules\\Microsoft_VC141_CRT_x86.msm'
57         trials = glob(redist_glob)
58         if len(trials) != 1:
59             sys.exit('There are more than one potential redist dirs.')
60         self.redist_path = trials[0]
61         self.component_num = 0
62         self.feature_properties = {
63             self.staging_dirs[0]: {
64                 'Id': 'MainProgram',
65                 'Title': 'Meson',
66                 'Description': 'Meson executables',
67                 'Level': '1',
68                 'Absent': 'disallow',
69             },
70             self.staging_dirs[1]: {
71                 'Id': 'NinjaProgram',
72                 'Title': 'Ninja',
73                 'Description': 'Ninja build tool',
74                 'Level': '1',
75             }
76         }
77         self.feature_components = {}
78         for sd in self.staging_dirs:
79             self.feature_components[sd] = []
80 
81     def build_dist(self):
82         for sdir in self.staging_dirs:
83             if os.path.exists(sdir):
84                 shutil.rmtree(sdir)
85         main_stage, ninja_stage = self.staging_dirs
86         modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))]
87         modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')]
88         modulestr = ','.join(modules)
89         python = shutil.which('python')
90         cxfreeze = os.path.join(os.path.dirname(python), "Scripts", "cxfreeze")
91         if not os.path.isfile(cxfreeze):
92             print("ERROR: This script requires cx_freeze module")
93             sys.exit(1)
94 
95         subprocess.check_call([python,
96                                cxfreeze,
97                                '--target-dir',
98                                main_stage,
99                                '--include-modules',
100                                modulestr,
101                                'meson.py'])
102         if not os.path.exists(os.path.join(main_stage, 'meson.exe')):
103             sys.exit('Meson exe missing from staging dir.')
104         os.mkdir(ninja_stage)
105         shutil.copy(shutil.which('ninja'), ninja_stage)
106         if not os.path.exists(os.path.join(ninja_stage, 'ninja.exe')):
107             sys.exit('Ninja exe missing from staging dir.')
108 
109     def generate_files(self):
110         self.root = ET.Element('Wix', {'xmlns': 'http://schemas.microsoft.com/wix/2006/wi'})
111         product = ET.SubElement(self.root, 'Product', {
112             'Name': self.product_name,
113             'Manufacturer': 'The Meson Development Team',
114             'Id': self.guid,
115             'UpgradeCode': self.update_guid,
116             'Language': '1033',
117             'Codepage':  '1252',
118             'Version': self.version,
119         })
120 
121         package = ET.SubElement(product, 'Package',  {
122             'Id': '*',
123             'Keywords': 'Installer',
124             'Description': 'Meson %s installer' % self.version,
125             'Comments': 'Meson is a high performance build system',
126             'Manufacturer': 'The Meson Development Team',
127             'InstallerVersion': '500',
128             'Languages': '1033',
129             'Compressed': 'yes',
130             'SummaryCodepage': '1252',
131         })
132 
133         if self.bytesize == 64:
134             package.set('Platform', 'x64')
135         ET.SubElement(product, 'Media', {
136             'Id': '1',
137             'Cabinet': 'meson.cab',
138             'EmbedCab': 'yes',
139         })
140         targetdir = ET.SubElement(product, 'Directory', {
141             'Id': 'TARGETDIR',
142             'Name': 'SourceDir',
143         })
144         progfiledir = ET.SubElement(targetdir, 'Directory', {
145             'Id': self.progfile_dir,
146         })
147         installdir = ET.SubElement(progfiledir, 'Directory', {
148             'Id': 'INSTALLDIR',
149             'Name': 'Meson',
150         })
151         ET.SubElement(installdir, 'Merge', {
152             'Id': 'VCRedist',
153             'SourceFile': self.redist_path,
154             'DiskId': '1',
155             'Language': '0',
156         })
157 
158         ET.SubElement(product, 'Property', {
159             'Id': 'WIXUI_INSTALLDIR',
160             'Value': 'INSTALLDIR',
161         })
162         ET.SubElement(product, 'UIRef', {
163             'Id': 'WixUI_FeatureTree',
164         })
165         for sd in self.staging_dirs:
166             assert(os.path.isdir(sd))
167         top_feature = ET.SubElement(product, 'Feature', {
168             'Id': 'Complete',
169             'Title': 'Meson ' + self.version,
170             'Description': 'The complete package',
171             'Display': 'expand',
172             'Level': '1',
173             'ConfigurableDirectory': 'INSTALLDIR',
174         })
175         for sd in self.staging_dirs:
176             nodes = {}
177             for root, dirs, files in os.walk(sd):
178                 cur_node = Node(dirs, files)
179                 nodes[root] = cur_node
180             self.create_xml(nodes, sd, installdir, sd)
181             self.build_features(nodes, top_feature, sd)
182         vcredist_feature = ET.SubElement(top_feature, 'Feature', {
183             'Id': 'VCRedist',
184             'Title': 'Visual C++ runtime',
185             'AllowAdvertise': 'no',
186             'Display': 'hidden',
187             'Level': '1',
188         })
189         ET.SubElement(vcredist_feature, 'MergeRef', {'Id': 'VCRedist'})
190         ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', xml_declaration=True)
191         # ElementTree can not do prettyprinting so do it manually
192         import xml.dom.minidom
193         doc = xml.dom.minidom.parse(self.main_xml)
194         with open(self.main_xml, 'w') as of:
195             of.write(doc.toprettyxml())
196 
197     def build_features(self, nodes, top_feature, staging_dir):
198         feature = ET.SubElement(top_feature, 'Feature',  self.feature_properties[staging_dir])
199         for component_id in self.feature_components[staging_dir]:
200             ET.SubElement(feature, 'ComponentRef', {
201                 'Id': component_id,
202             })
203 
204     def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir):
205         cur_node = nodes[current_dir]
206         if cur_node.files:
207             component_id = 'ApplicationFiles%d' % self.component_num
208             comp_xml_node = ET.SubElement(parent_xml_node, 'Component', {
209                 'Id': component_id,
210                 'Guid': gen_guid(),
211             })
212             self.feature_components[staging_dir].append(component_id)
213             if self.bytesize == 64:
214                 comp_xml_node.set('Win64', 'yes')
215             if self.component_num == 0:
216                 ET.SubElement(comp_xml_node, 'Environment', {
217                     'Id': 'Environment',
218                     'Name': 'PATH',
219                     'Part': 'last',
220                     'System': 'yes',
221                     'Action': 'set',
222                     'Value': '[INSTALLDIR]',
223                 })
224             self.component_num += 1
225             for f in cur_node.files:
226                 file_id = os.path.join(current_dir, f).replace('\\', '_').replace('#', '_').replace('-', '_')
227                 ET.SubElement(comp_xml_node, 'File', {
228                     'Id': file_id,
229                     'Name': f,
230                     'Source': os.path.join(current_dir, f),
231                 })
232 
233         for dirname in cur_node.dirs:
234             dir_id = os.path.join(current_dir, dirname).replace('\\', '_').replace('/', '_')
235             dir_node = ET.SubElement(parent_xml_node, 'Directory', {
236                 'Id': dir_id,
237                 'Name': dirname,
238             })
239             self.create_xml(nodes, os.path.join(current_dir, dirname), dir_node, staging_dir)
240 
241     def build_package(self):
242         wixdir = 'c:\\Program Files\\Wix Toolset v3.11\\bin'
243         if not os.path.isdir(wixdir):
244             wixdir = 'c:\\Program Files (x86)\\Wix Toolset v3.11\\bin'
245         if not os.path.isdir(wixdir):
246             print("ERROR: This script requires WIX")
247             sys.exit(1)
248         subprocess.check_call([os.path.join(wixdir, 'candle'), self.main_xml])
249         subprocess.check_call([os.path.join(wixdir, 'light'),
250                                '-ext', 'WixUIExtension',
251                                '-cultures:en-us',
252                                '-dWixUILicenseRtf=msi\\License.rtf',
253                                '-out', self.final_output,
254                                self.main_o])
255 
256 if __name__ == '__main__':
257     if not os.path.exists('meson.py'):
258         sys.exit(print('Run me in the top level source dir.'))
259     subprocess.check_call(['pip', 'install', '--upgrade', 'cx_freeze'])
260 
261     p = PackageGenerator()
262     p.build_dist()
263     p.generate_files()
264     p.build_package()
265
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/msi/createmsi.py b/msi/createmsi.py
--- a/msi/createmsi.py
+++ b/msi/createmsi.py
@@ -38,7 +38,7 @@
         self.product_name = 'Meson Build System'
         self.manufacturer = 'The Meson Development Team'
         self.version = coredata.version.replace('dev', '')
-        self.guid = 'DF5B3ECA-4A31-43E3-8CE4-97FC8A97212E'
+        self.guid = '*'
         self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2'
         self.main_xml = 'meson.wxs'
         self.main_o = 'meson.wixobj'

{"golden_diff": "diff --git a/msi/createmsi.py b/msi/createmsi.py\n--- a/msi/createmsi.py\n+++ b/msi/createmsi.py\n@@ -38,7 +38,7 @@\n         self.product_name = 'Meson Build System'\n         self.manufacturer = 'The Meson Development Team'\n         self.version = coredata.version.replace('dev', '')\n-        self.guid = 'DF5B3ECA-4A31-43E3-8CE4-97FC8A97212E'\n+        self.guid = '*'\n         self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2'\n         self.main_xml = 'meson.wxs'\n         self.main_o = 'meson.wixobj'\n", "issue": "MSI Installer: Support upgrade\nI just attempted to upgrade from meson `0.42.1` to `0.43` and was presented with this screen using the MSI installer\r\n\r\n![2017-10-11 14_37_47-windows installer](https://user-images.githubusercontent.com/3422847/31468523-efdaf1d2-ae92-11e7-8c93-04b1978479cf.png)\r\n\r\nAt the very least, that error message should be a bit more clear.\r\n\r\nBesides that - a huge :+1: for having a windows installer!\n", "before_files": [{"content": "#!/usr/bin/env python3\n\n# Copyright 2017 The Meson development team\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys, os, subprocess, shutil, uuid\nfrom glob import glob\nimport platform\nimport xml.etree.ElementTree as ET\n\nsys.path.append(os.getcwd())\nfrom mesonbuild import coredata\n\ndef gen_guid():\n    return str(uuid.uuid4()).upper()\n\nclass Node:\n    def __init__(self, dirs, files):\n        assert(isinstance(dirs, list))\n        assert(isinstance(files, list))\n        self.dirs = dirs\n        self.files = files\n\nclass PackageGenerator:\n\n    def __init__(self):\n        self.product_name = 'Meson Build System'\n        self.manufacturer = 'The Meson Development Team'\n        self.version = coredata.version.replace('dev', '')\n        self.guid = 'DF5B3ECA-4A31-43E3-8CE4-97FC8A97212E'\n        self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2'\n        self.main_xml = 'meson.wxs'\n        self.main_o = 'meson.wixobj'\n        self.bytesize = 32 if '32' in platform.architecture()[0] else 64\n        # rely on the environment variable since python architecture may not be the same as system architecture\n        if 'PROGRAMFILES(X86)' in os.environ:\n            self.bytesize = 64\n        self.final_output = 'meson-%s-%d.msi' % (self.version, self.bytesize)\n        self.staging_dirs = ['dist', 'dist2']\n        if self.bytesize == 64:\n            self.progfile_dir = 'ProgramFiles64Folder'\n            redist_glob = 'C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2017\\\\Community\\\\VC\\\\Redist\\\\MSVC\\\\*\\\\MergeModules\\\\Microsoft_VC141_CRT_x64.msm'\n        else:\n            self.progfile_dir = 'ProgramFilesFolder'\n            redist_glob = 'C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2017\\\\Community\\\\VC\\\\Redist\\\\MSVC\\\\*\\\\MergeModules\\\\Microsoft_VC141_CRT_x86.msm'\n        trials = glob(redist_glob)\n        if len(trials) != 1:\n            sys.exit('There are more than one potential redist dirs.')\n        self.redist_path = trials[0]\n        self.component_num = 0\n        self.feature_properties = {\n            self.staging_dirs[0]: {\n                'Id': 'MainProgram',\n                'Title': 'Meson',\n                'Description': 'Meson executables',\n                'Level': '1',\n                'Absent': 'disallow',\n            },\n            self.staging_dirs[1]: {\n                'Id': 'NinjaProgram',\n                'Title': 'Ninja',\n                'Description': 'Ninja build tool',\n                'Level': '1',\n            }\n        }\n        self.feature_components = {}\n        for sd in self.staging_dirs:\n            self.feature_components[sd] = []\n\n    def build_dist(self):\n        for sdir in self.staging_dirs:\n            if os.path.exists(sdir):\n                shutil.rmtree(sdir)\n        main_stage, ninja_stage = self.staging_dirs\n        modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))]\n        modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')]\n        modulestr = ','.join(modules)\n        python = shutil.which('python')\n        cxfreeze = os.path.join(os.path.dirname(python), \"Scripts\", \"cxfreeze\")\n        if not os.path.isfile(cxfreeze):\n            print(\"ERROR: This script requires cx_freeze module\")\n            sys.exit(1)\n\n        subprocess.check_call([python,\n                               cxfreeze,\n                               '--target-dir',\n                               main_stage,\n                               '--include-modules',\n                               modulestr,\n                               'meson.py'])\n        if not os.path.exists(os.path.join(main_stage, 'meson.exe')):\n            sys.exit('Meson exe missing from staging dir.')\n        os.mkdir(ninja_stage)\n        shutil.copy(shutil.which('ninja'), ninja_stage)\n        if not os.path.exists(os.path.join(ninja_stage, 'ninja.exe')):\n            sys.exit('Ninja exe missing from staging dir.')\n\n    def generate_files(self):\n        self.root = ET.Element('Wix', {'xmlns': 'http://schemas.microsoft.com/wix/2006/wi'})\n        product = ET.SubElement(self.root, 'Product', {\n            'Name': self.product_name,\n            'Manufacturer': 'The Meson Development Team',\n            'Id': self.guid,\n            'UpgradeCode': self.update_guid,\n            'Language': '1033',\n            'Codepage':  '1252',\n            'Version': self.version,\n        })\n\n        package = ET.SubElement(product, 'Package',  {\n            'Id': '*',\n            'Keywords': 'Installer',\n            'Description': 'Meson %s installer' % self.version,\n            'Comments': 'Meson is a high performance build system',\n            'Manufacturer': 'The Meson Development Team',\n            'InstallerVersion': '500',\n            'Languages': '1033',\n            'Compressed': 'yes',\n            'SummaryCodepage': '1252',\n        })\n\n        if self.bytesize == 64:\n            package.set('Platform', 'x64')\n        ET.SubElement(product, 'Media', {\n            'Id': '1',\n            'Cabinet': 'meson.cab',\n            'EmbedCab': 'yes',\n        })\n        targetdir = ET.SubElement(product, 'Directory', {\n            'Id': 'TARGETDIR',\n            'Name': 'SourceDir',\n        })\n        progfiledir = ET.SubElement(targetdir, 'Directory', {\n            'Id': self.progfile_dir,\n        })\n        installdir = ET.SubElement(progfiledir, 'Directory', {\n            'Id': 'INSTALLDIR',\n            'Name': 'Meson',\n        })\n        ET.SubElement(installdir, 'Merge', {\n            'Id': 'VCRedist',\n            'SourceFile': self.redist_path,\n            'DiskId': '1',\n            'Language': '0',\n        })\n\n        ET.SubElement(product, 'Property', {\n            'Id': 'WIXUI_INSTALLDIR',\n            'Value': 'INSTALLDIR',\n        })\n        ET.SubElement(product, 'UIRef', {\n            'Id': 'WixUI_FeatureTree',\n        })\n        for sd in self.staging_dirs:\n            assert(os.path.isdir(sd))\n        top_feature = ET.SubElement(product, 'Feature', {\n            'Id': 'Complete',\n            'Title': 'Meson ' + self.version,\n            'Description': 'The complete package',\n            'Display': 'expand',\n            'Level': '1',\n            'ConfigurableDirectory': 'INSTALLDIR',\n        })\n        for sd in self.staging_dirs:\n            nodes = {}\n            for root, dirs, files in os.walk(sd):\n                cur_node = Node(dirs, files)\n                nodes[root] = cur_node\n            self.create_xml(nodes, sd, installdir, sd)\n            self.build_features(nodes, top_feature, sd)\n        vcredist_feature = ET.SubElement(top_feature, 'Feature', {\n            'Id': 'VCRedist',\n            'Title': 'Visual C++ runtime',\n            'AllowAdvertise': 'no',\n            'Display': 'hidden',\n            'Level': '1',\n        })\n        ET.SubElement(vcredist_feature, 'MergeRef', {'Id': 'VCRedist'})\n        ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', xml_declaration=True)\n        # ElementTree can not do prettyprinting so do it manually\n        import xml.dom.minidom\n        doc = xml.dom.minidom.parse(self.main_xml)\n        with open(self.main_xml, 'w') as of:\n            of.write(doc.toprettyxml())\n\n    def build_features(self, nodes, top_feature, staging_dir):\n        feature = ET.SubElement(top_feature, 'Feature',  self.feature_properties[staging_dir])\n        for component_id in self.feature_components[staging_dir]:\n            ET.SubElement(feature, 'ComponentRef', {\n                'Id': component_id,\n            })\n\n    def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir):\n        cur_node = nodes[current_dir]\n        if cur_node.files:\n            component_id = 'ApplicationFiles%d' % self.component_num\n            comp_xml_node = ET.SubElement(parent_xml_node, 'Component', {\n                'Id': component_id,\n                'Guid': gen_guid(),\n            })\n            self.feature_components[staging_dir].append(component_id)\n            if self.bytesize == 64:\n                comp_xml_node.set('Win64', 'yes')\n            if self.component_num == 0:\n                ET.SubElement(comp_xml_node, 'Environment', {\n                    'Id': 'Environment',\n                    'Name': 'PATH',\n                    'Part': 'last',\n                    'System': 'yes',\n                    'Action': 'set',\n                    'Value': '[INSTALLDIR]',\n                })\n            self.component_num += 1\n            for f in cur_node.files:\n                file_id = os.path.join(current_dir, f).replace('\\\\', '_').replace('#', '_').replace('-', '_')\n                ET.SubElement(comp_xml_node, 'File', {\n                    'Id': file_id,\n                    'Name': f,\n                    'Source': os.path.join(current_dir, f),\n                })\n\n        for dirname in cur_node.dirs:\n            dir_id = os.path.join(current_dir, dirname).replace('\\\\', '_').replace('/', '_')\n            dir_node = ET.SubElement(parent_xml_node, 'Directory', {\n                'Id': dir_id,\n                'Name': dirname,\n            })\n            self.create_xml(nodes, os.path.join(current_dir, dirname), dir_node, staging_dir)\n\n    def build_package(self):\n        wixdir = 'c:\\\\Program Files\\\\Wix Toolset v3.11\\\\bin'\n        if not os.path.isdir(wixdir):\n            wixdir = 'c:\\\\Program Files (x86)\\\\Wix Toolset v3.11\\\\bin'\n        if not os.path.isdir(wixdir):\n            print(\"ERROR: This script requires WIX\")\n            sys.exit(1)\n        subprocess.check_call([os.path.join(wixdir, 'candle'), self.main_xml])\n        subprocess.check_call([os.path.join(wixdir, 'light'),\n                               '-ext', 'WixUIExtension',\n                               '-cultures:en-us',\n                               '-dWixUILicenseRtf=msi\\\\License.rtf',\n                               '-out', self.final_output,\n                               self.main_o])\n\nif __name__ == '__main__':\n    if not os.path.exists('meson.py'):\n        sys.exit(print('Run me in the top level source dir.'))\n    subprocess.check_call(['pip', 'install', '--upgrade', 'cx_freeze'])\n\n    p = PackageGenerator()\n    p.build_dist()\n    p.generate_files()\n    p.build_package()\n", "path": "msi/createmsi.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\n# Copyright 2017 The Meson development team\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys, os, subprocess, shutil, uuid\nfrom glob import glob\nimport platform\nimport xml.etree.ElementTree as ET\n\nsys.path.append(os.getcwd())\nfrom mesonbuild import coredata\n\ndef gen_guid():\n    return str(uuid.uuid4()).upper()\n\nclass Node:\n    def __init__(self, dirs, files):\n        assert(isinstance(dirs, list))\n        assert(isinstance(files, list))\n        self.dirs = dirs\n        self.files = files\n\nclass PackageGenerator:\n\n    def __init__(self):\n        self.product_name = 'Meson Build System'\n        self.manufacturer = 'The Meson Development Team'\n        self.version = coredata.version.replace('dev', '')\n        self.guid = '*'\n        self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2'\n        self.main_xml = 'meson.wxs'\n        self.main_o = 'meson.wixobj'\n        self.bytesize = 32 if '32' in platform.architecture()[0] else 64\n        # rely on the environment variable since python architecture may not be the same as system architecture\n        if 'PROGRAMFILES(X86)' in os.environ:\n            self.bytesize = 64\n        self.final_output = 'meson-%s-%d.msi' % (self.version, self.bytesize)\n        self.staging_dirs = ['dist', 'dist2']\n        if self.bytesize == 64:\n            self.progfile_dir = 'ProgramFiles64Folder'\n            redist_glob = 'C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2017\\\\Community\\\\VC\\\\Redist\\\\MSVC\\\\*\\\\MergeModules\\\\Microsoft_VC141_CRT_x64.msm'\n        else:\n            self.progfile_dir = 'ProgramFilesFolder'\n            redist_glob = 'C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2017\\\\Community\\\\VC\\\\Redist\\\\MSVC\\\\*\\\\MergeModules\\\\Microsoft_VC141_CRT_x86.msm'\n        trials = glob(redist_glob)\n        if len(trials) != 1:\n            sys.exit('There are more than one potential redist dirs.')\n        self.redist_path = trials[0]\n        self.component_num = 0\n        self.feature_properties = {\n            self.staging_dirs[0]: {\n                'Id': 'MainProgram',\n                'Title': 'Meson',\n                'Description': 'Meson executables',\n                'Level': '1',\n                'Absent': 'disallow',\n            },\n            self.staging_dirs[1]: {\n                'Id': 'NinjaProgram',\n                'Title': 'Ninja',\n                'Description': 'Ninja build tool',\n                'Level': '1',\n            }\n        }\n        self.feature_components = {}\n        for sd in self.staging_dirs:\n            self.feature_components[sd] = []\n\n    def build_dist(self):\n        for sdir in self.staging_dirs:\n            if os.path.exists(sdir):\n                shutil.rmtree(sdir)\n        main_stage, ninja_stage = self.staging_dirs\n        modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))]\n        modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')]\n        modulestr = ','.join(modules)\n        python = shutil.which('python')\n        cxfreeze = os.path.join(os.path.dirname(python), \"Scripts\", \"cxfreeze\")\n        if not os.path.isfile(cxfreeze):\n            print(\"ERROR: This script requires cx_freeze module\")\n            sys.exit(1)\n\n        subprocess.check_call([python,\n                               cxfreeze,\n                               '--target-dir',\n                               main_stage,\n                               '--include-modules',\n                               modulestr,\n                               'meson.py'])\n        if not os.path.exists(os.path.join(main_stage, 'meson.exe')):\n            sys.exit('Meson exe missing from staging dir.')\n        os.mkdir(ninja_stage)\n        shutil.copy(shutil.which('ninja'), ninja_stage)\n        if not os.path.exists(os.path.join(ninja_stage, 'ninja.exe')):\n            sys.exit('Ninja exe missing from staging dir.')\n\n    def generate_files(self):\n        self.root = ET.Element('Wix', {'xmlns': 'http://schemas.microsoft.com/wix/2006/wi'})\n        product = ET.SubElement(self.root, 'Product', {\n            'Name': self.product_name,\n            'Manufacturer': 'The Meson Development Team',\n            'Id': self.guid,\n            'UpgradeCode': self.update_guid,\n            'Language': '1033',\n            'Codepage':  '1252',\n            'Version': self.version,\n        })\n\n        package = ET.SubElement(product, 'Package',  {\n            'Id': '*',\n            'Keywords': 'Installer',\n            'Description': 'Meson %s installer' % self.version,\n            'Comments': 'Meson is a high performance build system',\n            'Manufacturer': 'The Meson Development Team',\n            'InstallerVersion': '500',\n            'Languages': '1033',\n            'Compressed': 'yes',\n            'SummaryCodepage': '1252',\n        })\n\n        if self.bytesize == 64:\n            package.set('Platform', 'x64')\n        ET.SubElement(product, 'Media', {\n            'Id': '1',\n            'Cabinet': 'meson.cab',\n            'EmbedCab': 'yes',\n        })\n        targetdir = ET.SubElement(product, 'Directory', {\n            'Id': 'TARGETDIR',\n            'Name': 'SourceDir',\n        })\n        progfiledir = ET.SubElement(targetdir, 'Directory', {\n            'Id': self.progfile_dir,\n        })\n        installdir = ET.SubElement(progfiledir, 'Directory', {\n            'Id': 'INSTALLDIR',\n            'Name': 'Meson',\n        })\n        ET.SubElement(installdir, 'Merge', {\n            'Id': 'VCRedist',\n            'SourceFile': self.redist_path,\n            'DiskId': '1',\n            'Language': '0',\n        })\n\n        ET.SubElement(product, 'Property', {\n            'Id': 'WIXUI_INSTALLDIR',\n            'Value': 'INSTALLDIR',\n        })\n        ET.SubElement(product, 'UIRef', {\n            'Id': 'WixUI_FeatureTree',\n        })\n        for sd in self.staging_dirs:\n            assert(os.path.isdir(sd))\n        top_feature = ET.SubElement(product, 'Feature', {\n            'Id': 'Complete',\n            'Title': 'Meson ' + self.version,\n            'Description': 'The complete package',\n            'Display': 'expand',\n            'Level': '1',\n            'ConfigurableDirectory': 'INSTALLDIR',\n        })\n        for sd in self.staging_dirs:\n            nodes = {}\n            for root, dirs, files in os.walk(sd):\n                cur_node = Node(dirs, files)\n                nodes[root] = cur_node\n            self.create_xml(nodes, sd, installdir, sd)\n            self.build_features(nodes, top_feature, sd)\n        vcredist_feature = ET.SubElement(top_feature, 'Feature', {\n            'Id': 'VCRedist',\n            'Title': 'Visual C++ runtime',\n            'AllowAdvertise': 'no',\n            'Display': 'hidden',\n            'Level': '1',\n        })\n        ET.SubElement(vcredist_feature, 'MergeRef', {'Id': 'VCRedist'})\n        ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', xml_declaration=True)\n        # ElementTree can not do prettyprinting so do it manually\n        import xml.dom.minidom\n        doc = xml.dom.minidom.parse(self.main_xml)\n        with open(self.main_xml, 'w') as of:\n            of.write(doc.toprettyxml())\n\n    def build_features(self, nodes, top_feature, staging_dir):\n        feature = ET.SubElement(top_feature, 'Feature',  self.feature_properties[staging_dir])\n        for component_id in self.feature_components[staging_dir]:\n            ET.SubElement(feature, 'ComponentRef', {\n                'Id': component_id,\n            })\n\n    def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir):\n        cur_node = nodes[current_dir]\n        if cur_node.files:\n            component_id = 'ApplicationFiles%d' % self.component_num\n            comp_xml_node = ET.SubElement(parent_xml_node, 'Component', {\n                'Id': component_id,\n                'Guid': gen_guid(),\n            })\n            self.feature_components[staging_dir].append(component_id)\n            if self.bytesize == 64:\n                comp_xml_node.set('Win64', 'yes')\n            if self.component_num == 0:\n                ET.SubElement(comp_xml_node, 'Environment', {\n                    'Id': 'Environment',\n                    'Name': 'PATH',\n                    'Part': 'last',\n                    'System': 'yes',\n                    'Action': 'set',\n                    'Value': '[INSTALLDIR]',\n                })\n            self.component_num += 1\n            for f in cur_node.files:\n                file_id = os.path.join(current_dir, f).replace('\\\\', '_').replace('#', '_').replace('-', '_')\n                ET.SubElement(comp_xml_node, 'File', {\n                    'Id': file_id,\n                    'Name': f,\n                    'Source': os.path.join(current_dir, f),\n                })\n\n        for dirname in cur_node.dirs:\n            dir_id = os.path.join(current_dir, dirname).replace('\\\\', '_').replace('/', '_')\n            dir_node = ET.SubElement(parent_xml_node, 'Directory', {\n                'Id': dir_id,\n                'Name': dirname,\n            })\n            self.create_xml(nodes, os.path.join(current_dir, dirname), dir_node, staging_dir)\n\n    def build_package(self):\n        wixdir = 'c:\\\\Program Files\\\\Wix Toolset v3.11\\\\bin'\n        if not os.path.isdir(wixdir):\n            wixdir = 'c:\\\\Program Files (x86)\\\\Wix Toolset v3.11\\\\bin'\n        if not os.path.isdir(wixdir):\n            print(\"ERROR: This script requires WIX\")\n            sys.exit(1)\n        subprocess.check_call([os.path.join(wixdir, 'candle'), self.main_xml])\n        subprocess.check_call([os.path.join(wixdir, 'light'),\n                               '-ext', 'WixUIExtension',\n                               '-cultures:en-us',\n                               '-dWixUILicenseRtf=msi\\\\License.rtf',\n                               '-out', self.final_output,\n                               self.main_o])\n\nif __name__ == '__main__':\n    if not os.path.exists('meson.py'):\n        sys.exit(print('Run me in the top level source dir.'))\n    subprocess.check_call(['pip', 'install', '--upgrade', 'cx_freeze'])\n\n    p = PackageGenerator()\n    p.build_dist()\n    p.generate_files()\n    p.build_package()\n", "path": "msi/createmsi.py"}]}