Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Side by Side Diff: tools/create_windows_installer.py

Issue 1130963003: Remove gyp entries for editor and java based analyzer (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/compiler_scripts/generate_source_list.py ('k') | tools/mac_build_editor_bundle.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a
5 # BSD-style license that can be found in the LICENSE file.
6
7 # A script to generate a windows installer for the editor bundle.
8 # As input the script takes a zip file, a version and the location
9 # to store the resulting msi file in.
10 #
11 # Usage: ./tools/create_windows_installer.py --version <version>
12 # --zip_file_location <zip_file> --msi_location <output>
13 # [--wix_bin <wix_bin_location>]
14 # [--print_wxs]
15 #
16 # This script assumes that wix is either in path or passed in as --wix_bin.
17 # You can get wix from http://wixtoolset.org/.
18
19 import optparse
20 import os
21 import subprocess
22 import sys
23 import utils
24 import zipfile
25
26 # This should _never_ change, please don't change this value.
27 UPGRADE_CODE = '7bacdc33-2e76-4f36-a206-ea58220c0b44'
28
29 # The content of the xml
30 xml_content = []
31
32 # The components we want to add to our feature.
33 feature_components = []
34
35 # Indentation level, each level is indented 2 spaces
36 current_indentation = 0
37
38 def GetOptions():
39 options = optparse.OptionParser(usage='usage: %prog [options]')
40 options.add_option("--zip_file_location",
41 help='Where the zip file including the editor is located.')
42 options.add_option("--input_directory",
43 help='Directory where all the files needed is located.')
44 options.add_option("--msi_location",
45 help='Where to store the resulting msi.')
46 options.add_option("--version",
47 help='The version specified as Major.Minor.Build.Patch.')
48 options.add_option("--wix_bin",
49 help='The location of the wix binary files.')
50 options.add_option("--print_wxs", action="store_true", dest="print_wxs",
51 default=False,
52 help="Prints the generated wxs to stdout.")
53 (options, args) = options.parse_args()
54 if len(args) > 0:
55 raise Exception("This script takes no arguments, only options")
56 ValidateOptions(options)
57 return options
58
59 def ValidateOptions(options):
60 if not options.version:
61 raise Exception('You must supply a version')
62 if options.zip_file_location and options.input_directory:
63 raise Exception('Please pass either zip_file_location or input_directory')
64 if not options.zip_file_location and not options.input_directory:
65 raise Exception('Please pass either zip_file_location or input_directory')
66 if (options.zip_file_location and
67 not os.path.isfile(options.zip_file_location)):
68 raise Exception('Passed in zip file not found')
69 if (options.input_directory and
70 not os.path.isdir(options.input_directory)):
71 raise Exception('Passed in directory not found')
72
73 def GetInputDirectory(options, temp_dir):
74 if options.zip_file_location:
75 ExtractZipFile(options.zip_file_location, temp_dir)
76 return os.path.join(temp_dir, 'dart')
77 return options.input_directory
78
79 # We combine the build and patch into a single entry since
80 # the windows installer does _not_ consider a change in Patch
81 # to require a new install.
82 # In addition to that, the limits on the size are:
83 # Major: 256
84 # Minor: 256
85 # Patch: 65536
86 # To circumvent this we create the version like this:
87 # Major.Minor.X
88 # from "major.minor.patch-prerelease.prerelease_patch"
89 # where X is "patch<<10 + prerelease<<5 + prerelease_patch"
90 # Example version 1.2.4-dev.2.3 will go to 1.2.4163
91 def GetMicrosoftProductVersion(version):
92 version_parts = version.split('.')
93 if len(version_parts) is not 5:
94 raise Exception(
95 "Version string (%s) does not follow specification" % version)
96 (major, minor, patch, prerelease, prerelease_patch) = map(int, version_parts)
97
98 if major > 255 or minor > 255:
99 raise Exception('Major/Minor can not be above 256')
100 if patch > 63:
101 raise Exception('Patch can not be above 63')
102 if prerelease > 31:
103 raise Exception('Prerelease can not be above 31')
104 if prerelease_patch > 31:
105 raise Exception('PrereleasePatch can not be above 31')
106
107 combined = (patch << 10) + (prerelease << 5) + prerelease_patch
108 return '%s.%s.%s' % (major, minor, combined)
109
110 # Append using the current indentation level
111 def Append(data, new_line=True):
112 str = ((' ' * current_indentation) +
113 data +
114 ('\n' if new_line else ''))
115 xml_content.append(str)
116
117 # Append without any indentation at the current position
118 def AppendRaw(data, new_line=True):
119 xml_content.append(data + ('\n' if new_line else ''))
120
121 def AppendComment(comment):
122 Append('<!--%s-->' % comment)
123
124 def AppendBlankLine():
125 Append('')
126
127 def GetContent():
128 return ''.join(xml_content)
129
130 def XmlHeader():
131 Append('<?xml version="1.0" encoding="UTF-8"?>')
132
133 def TagIndent(str, indentation_string):
134 return ' ' * len(indentation_string) + str
135
136 def IncreaseIndentation():
137 global current_indentation
138 current_indentation += 1
139
140 def DecreaseIndentation():
141 global current_indentation
142 current_indentation -= 1
143
144 class WixAndProduct(object):
145 def __init__(self, version):
146 self.version = version
147 self.product_name = 'Dart Editor'
148 self.manufacturer = 'Google Inc.'
149 self.upgrade_code = UPGRADE_CODE
150
151 def __enter__(self):
152 self.start_wix()
153 self.start_product()
154
155 def __exit__(self, *_):
156 self.close_product()
157 self.close_wix()
158
159 def get_product_id(self):
160 # This needs to change on every install to guarantee that
161 # we get a full uninstall + reinstall
162 # We let wix choose. If we need to do patch releases later on
163 # we need to retain the value over several installs.
164 return '*'
165
166 def start_product(self):
167 product = '<Product '
168 Append(product, new_line=False)
169 AppendRaw('Id="%s"' % self.get_product_id())
170 Append(TagIndent('Version="%s"' % self.version, product))
171 Append(TagIndent('Name="%s"' % self.product_name, product))
172 Append(TagIndent('UpgradeCode="%s"' % self.upgrade_code,
173 product))
174 Append(TagIndent('Language="1033"', product))
175 Append(TagIndent('Manufacturer="%s"' % self.manufacturer,
176 product),
177 new_line=False)
178 AppendRaw('>')
179 IncreaseIndentation()
180
181 def close_product(self):
182 DecreaseIndentation()
183 Append('</Product>')
184
185 def start_wix(self):
186 Append('<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">')
187 IncreaseIndentation()
188
189 def close_wix(self):
190 DecreaseIndentation()
191 Append('</Wix>')
192
193 class Directory(object):
194 def __init__(self, id, name=None):
195 self.id = id
196 self.name = name
197
198 def __enter__(self):
199 directory = '<Directory '
200 Append(directory, new_line=False)
201 AppendRaw('Id="%s"' % self.id, new_line=self.name is not None)
202 if self.name:
203 Append(TagIndent('Name="%s"' % self.name, directory), new_line=False)
204 AppendRaw('>')
205 IncreaseIndentation()
206
207 def __exit__(self, *_):
208 DecreaseIndentation()
209 Append('</Directory>')
210
211 class Component(object):
212 def __init__(self, id):
213 self.id = 'CMP_%s' % id
214
215 def __enter__(self):
216 component = '<Component '
217 Append(component, new_line=False)
218 AppendRaw('Id="%s"' % self.id)
219 Append(TagIndent('Guid="*">', component))
220 IncreaseIndentation()
221
222 def __exit__(self, *_):
223 DecreaseIndentation()
224 Append('</Component>')
225 feature_components.append(self.id)
226
227 class Feature(object):
228 def __enter__(self):
229 feature = '<Feature '
230 Append(feature, new_line=False)
231 AppendRaw('Id="MainFeature"')
232 Append(TagIndent('Title="Dart Editor"', feature))
233 # Install by default
234 Append(TagIndent('Level="1">', feature))
235 IncreaseIndentation()
236
237 def __exit__(self, *_):
238 DecreaseIndentation()
239 Append('</Feature>')
240
241 def Package():
242 package = '<Package '
243 Append(package, new_line=False)
244 AppendRaw('InstallerVersion="301"')
245 Append(TagIndent('Compressed="yes" />', package))
246
247 def MediaTemplate():
248 Append('<MediaTemplate EmbedCab="yes" />')
249
250 def File(name, id):
251 file = '<File '
252 Append(file, new_line=False)
253 AppendRaw('Id="FILE_%s"' % id)
254 Append(TagIndent('Source="%s"' % name, file))
255 Append(TagIndent('KeyPath="yes" />', file))
256
257 def Shortcut(id, name, ref):
258 shortcut = '<Shortcut '
259 Append(shortcut, new_line=False)
260 AppendRaw('Id="%s"' % id)
261 Append(TagIndent('Name="%s"' % name, shortcut))
262 Append(TagIndent('Target="%s" />' % ref, shortcut))
263
264 def RemoveFolder(id):
265 remove = '<RemoveFolder '
266 Append(remove, new_line=False)
267 AppendRaw('Id="%s"' % id)
268 Append(TagIndent('On="uninstall" />', remove))
269
270 def RegistryEntry(location):
271 registry = '<RegistryValue '
272 Append(registry, new_line=False)
273 AppendRaw('Root="HKCU"')
274 Append(TagIndent('Key="Software\\Microsoft\\%s"' % location, registry))
275 Append(TagIndent('Name="installed"', registry))
276 Append(TagIndent('Type="integer"', registry))
277 Append(TagIndent('Value="1"', registry))
278 Append(TagIndent('KeyPath="yes" />', registry))
279
280
281 def MajorUpgrade():
282 upgrade = '<MajorUpgrade '
283 Append(upgrade, new_line=False)
284 down_message = 'You already have a never version installed.'
285 AppendRaw('DowngradeErrorMessage="%s" />' % down_message)
286
287
288 # This is a very simplistic id generation.
289 # Unfortunately there is no easy way to generate good names,
290 # since there is a 72 character limit, and we have way longer
291 # paths. We don't really have an issue with files and ids across
292 # releases since we do full installs.
293 counter = 0
294 def FileToId(name):
295 global counter
296 counter += 1
297 return '%s' % counter
298
299 def ListFiles(path):
300 for entry in os.listdir(path):
301 full_path = os.path.join(path, entry)
302 id = FileToId(full_path)
303 if os.path.isdir(full_path):
304 with Directory('DIR_%s' % id, entry):
305 ListFiles(full_path)
306 elif os.path.isfile(full_path):
307 # We assume 1 file per component, a File is always a KeyPath.
308 # A KeyPath on a file makes sure that we can always repair and
309 # remove that file in a consistent manner. A component
310 # can only have one child with a KeyPath.
311 with Component(id):
312 File(full_path, id)
313
314 def ComponentRefs():
315 for component in feature_components:
316 Append('<ComponentRef Id="%s" />' % component)
317
318 def ExtractZipFile(zip, temp_dir):
319 print 'Extracting files'
320 f = zipfile.ZipFile(zip)
321 f.extractall(temp_dir)
322 f.close()
323
324 def GenerateInstaller(wxs_content, options, temp_dir):
325 wxs_file = os.path.join(temp_dir, 'installer.wxs')
326 wixobj_file = os.path.join(temp_dir, 'installer.wixobj')
327 print 'Saving wxs output to: %s' % wxs_file
328 with open(wxs_file, 'w') as f:
329 f.write(wxs_content)
330
331 candle_bin = 'candle.exe'
332 light_bin = 'light.exe'
333 if options.wix_bin:
334 candle_bin = os.path.join(options.wix_bin, 'candle.exe')
335 light_bin = os.path.join(options.wix_bin, 'light.exe')
336 print 'Calling candle on %s' % wxs_file
337 subprocess.check_call('%s %s -o %s' % (candle_bin, wxs_file,
338 wixobj_file))
339 print 'Calling light on %s' % wixobj_file
340 subprocess.check_call('%s %s -o %s' % (light_bin, wixobj_file,
341 options.msi_location))
342 print 'Created msi file to %s' % options.msi_location
343
344
345 def Main(argv):
346 if sys.platform != 'win32':
347 raise Exception("This script can only be run on windows")
348 options = GetOptions()
349 version = GetMicrosoftProductVersion(options.version)
350 with utils.TempDir('installer') as temp_dir:
351 input_location = GetInputDirectory(options, temp_dir)
352 print "Generating wix XML"
353 XmlHeader()
354 with WixAndProduct(version):
355 AppendBlankLine()
356 Package()
357 MediaTemplate()
358 AppendComment('We always do a major upgrade, at least for now')
359 MajorUpgrade()
360
361 AppendComment('Directory structure')
362 with Directory('TARGETDIR', 'SourceDir'):
363 with Directory('ProgramFilesFolder'):
364 with Directory('RootInstallDir', 'Dart Editor'):
365 AppendComment("Add all files and directories")
366 print 'Installing files and directories in xml'
367 ListFiles(input_location)
368 AppendBlankLine()
369 AppendComment("Create shortcuts")
370 with Directory('ProgramMenuFolder'):
371 with Directory('ShortcutFolder', 'Dart Editor'):
372 with Component('shortcut'):
373 # When generating a shortcut we need an entry with
374 # a KeyPath (RegistryEntry) below - to be able to remove
375 # the shortcut again. The RemoveFolder tag is needed
376 # to clean up everything
377 Shortcut('editor_shortcut', 'Dart Editor',
378 '[RootInstallDir]DartEditor.exe')
379 RemoveFolder('RemoveShortcuts')
380 RegistryEntry('DartEditor')
381 with Feature():
382 # We have only one feature, and it consists of all the
383 # files=components we have listed above"
384 ComponentRefs()
385 xml = GetContent()
386 if options.print_wxs:
387 print xml
388 GenerateInstaller(xml, options, temp_dir)
389
390 if __name__ == '__main__':
391 sys.exit(Main(sys.argv))
OLDNEW
« no previous file with comments | « tools/compiler_scripts/generate_source_list.py ('k') | tools/mac_build_editor_bundle.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698