Index: tools/create_windows_installer.py |
diff --git a/tools/create_windows_installer.py b/tools/create_windows_installer.py |
deleted file mode 100644 |
index 64fe9947b0c121353287b1dff824ee8ae1559896..0000000000000000000000000000000000000000 |
--- a/tools/create_windows_installer.py |
+++ /dev/null |
@@ -1,391 +0,0 @@ |
-#!/usr/bin/env python |
-# |
-# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
-# for details. All rights reserved. Use of this source code is governed by a |
-# BSD-style license that can be found in the LICENSE file. |
- |
-# A script to generate a windows installer for the editor bundle. |
-# As input the script takes a zip file, a version and the location |
-# to store the resulting msi file in. |
-# |
-# Usage: ./tools/create_windows_installer.py --version <version> |
-# --zip_file_location <zip_file> --msi_location <output> |
-# [--wix_bin <wix_bin_location>] |
-# [--print_wxs] |
-# |
-# This script assumes that wix is either in path or passed in as --wix_bin. |
-# You can get wix from http://wixtoolset.org/. |
- |
-import optparse |
-import os |
-import subprocess |
-import sys |
-import utils |
-import zipfile |
- |
-# This should _never_ change, please don't change this value. |
-UPGRADE_CODE = '7bacdc33-2e76-4f36-a206-ea58220c0b44' |
- |
-# The content of the xml |
-xml_content = [] |
- |
-# The components we want to add to our feature. |
-feature_components = [] |
- |
-# Indentation level, each level is indented 2 spaces |
-current_indentation = 0 |
- |
-def GetOptions(): |
- options = optparse.OptionParser(usage='usage: %prog [options]') |
- options.add_option("--zip_file_location", |
- help='Where the zip file including the editor is located.') |
- options.add_option("--input_directory", |
- help='Directory where all the files needed is located.') |
- options.add_option("--msi_location", |
- help='Where to store the resulting msi.') |
- options.add_option("--version", |
- help='The version specified as Major.Minor.Build.Patch.') |
- options.add_option("--wix_bin", |
- help='The location of the wix binary files.') |
- options.add_option("--print_wxs", action="store_true", dest="print_wxs", |
- default=False, |
- help="Prints the generated wxs to stdout.") |
- (options, args) = options.parse_args() |
- if len(args) > 0: |
- raise Exception("This script takes no arguments, only options") |
- ValidateOptions(options) |
- return options |
- |
-def ValidateOptions(options): |
- if not options.version: |
- raise Exception('You must supply a version') |
- if options.zip_file_location and options.input_directory: |
- raise Exception('Please pass either zip_file_location or input_directory') |
- if not options.zip_file_location and not options.input_directory: |
- raise Exception('Please pass either zip_file_location or input_directory') |
- if (options.zip_file_location and |
- not os.path.isfile(options.zip_file_location)): |
- raise Exception('Passed in zip file not found') |
- if (options.input_directory and |
- not os.path.isdir(options.input_directory)): |
- raise Exception('Passed in directory not found') |
- |
-def GetInputDirectory(options, temp_dir): |
- if options.zip_file_location: |
- ExtractZipFile(options.zip_file_location, temp_dir) |
- return os.path.join(temp_dir, 'dart') |
- return options.input_directory |
- |
-# We combine the build and patch into a single entry since |
-# the windows installer does _not_ consider a change in Patch |
-# to require a new install. |
-# In addition to that, the limits on the size are: |
-# Major: 256 |
-# Minor: 256 |
-# Patch: 65536 |
-# To circumvent this we create the version like this: |
-# Major.Minor.X |
-# from "major.minor.patch-prerelease.prerelease_patch" |
-# where X is "patch<<10 + prerelease<<5 + prerelease_patch" |
-# Example version 1.2.4-dev.2.3 will go to 1.2.4163 |
-def GetMicrosoftProductVersion(version): |
- version_parts = version.split('.') |
- if len(version_parts) is not 5: |
- raise Exception( |
- "Version string (%s) does not follow specification" % version) |
- (major, minor, patch, prerelease, prerelease_patch) = map(int, version_parts) |
- |
- if major > 255 or minor > 255: |
- raise Exception('Major/Minor can not be above 256') |
- if patch > 63: |
- raise Exception('Patch can not be above 63') |
- if prerelease > 31: |
- raise Exception('Prerelease can not be above 31') |
- if prerelease_patch > 31: |
- raise Exception('PrereleasePatch can not be above 31') |
- |
- combined = (patch << 10) + (prerelease << 5) + prerelease_patch |
- return '%s.%s.%s' % (major, minor, combined) |
- |
-# Append using the current indentation level |
-def Append(data, new_line=True): |
- str = ((' ' * current_indentation) + |
- data + |
- ('\n' if new_line else '')) |
- xml_content.append(str) |
- |
-# Append without any indentation at the current position |
-def AppendRaw(data, new_line=True): |
- xml_content.append(data + ('\n' if new_line else '')) |
- |
-def AppendComment(comment): |
- Append('<!--%s-->' % comment) |
- |
-def AppendBlankLine(): |
- Append('') |
- |
-def GetContent(): |
- return ''.join(xml_content) |
- |
-def XmlHeader(): |
- Append('<?xml version="1.0" encoding="UTF-8"?>') |
- |
-def TagIndent(str, indentation_string): |
- return ' ' * len(indentation_string) + str |
- |
-def IncreaseIndentation(): |
- global current_indentation |
- current_indentation += 1 |
- |
-def DecreaseIndentation(): |
- global current_indentation |
- current_indentation -= 1 |
- |
-class WixAndProduct(object): |
- def __init__(self, version): |
- self.version = version |
- self.product_name = 'Dart Editor' |
- self.manufacturer = 'Google Inc.' |
- self.upgrade_code = UPGRADE_CODE |
- |
- def __enter__(self): |
- self.start_wix() |
- self.start_product() |
- |
- def __exit__(self, *_): |
- self.close_product() |
- self.close_wix() |
- |
- def get_product_id(self): |
- # This needs to change on every install to guarantee that |
- # we get a full uninstall + reinstall |
- # We let wix choose. If we need to do patch releases later on |
- # we need to retain the value over several installs. |
- return '*' |
- |
- def start_product(self): |
- product = '<Product ' |
- Append(product, new_line=False) |
- AppendRaw('Id="%s"' % self.get_product_id()) |
- Append(TagIndent('Version="%s"' % self.version, product)) |
- Append(TagIndent('Name="%s"' % self.product_name, product)) |
- Append(TagIndent('UpgradeCode="%s"' % self.upgrade_code, |
- product)) |
- Append(TagIndent('Language="1033"', product)) |
- Append(TagIndent('Manufacturer="%s"' % self.manufacturer, |
- product), |
- new_line=False) |
- AppendRaw('>') |
- IncreaseIndentation() |
- |
- def close_product(self): |
- DecreaseIndentation() |
- Append('</Product>') |
- |
- def start_wix(self): |
- Append('<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">') |
- IncreaseIndentation() |
- |
- def close_wix(self): |
- DecreaseIndentation() |
- Append('</Wix>') |
- |
-class Directory(object): |
- def __init__(self, id, name=None): |
- self.id = id |
- self.name = name |
- |
- def __enter__(self): |
- directory = '<Directory ' |
- Append(directory, new_line=False) |
- AppendRaw('Id="%s"' % self.id, new_line=self.name is not None) |
- if self.name: |
- Append(TagIndent('Name="%s"' % self.name, directory), new_line=False) |
- AppendRaw('>') |
- IncreaseIndentation() |
- |
- def __exit__(self, *_): |
- DecreaseIndentation() |
- Append('</Directory>') |
- |
-class Component(object): |
- def __init__(self, id): |
- self.id = 'CMP_%s' % id |
- |
- def __enter__(self): |
- component = '<Component ' |
- Append(component, new_line=False) |
- AppendRaw('Id="%s"' % self.id) |
- Append(TagIndent('Guid="*">', component)) |
- IncreaseIndentation() |
- |
- def __exit__(self, *_): |
- DecreaseIndentation() |
- Append('</Component>') |
- feature_components.append(self.id) |
- |
-class Feature(object): |
- def __enter__(self): |
- feature = '<Feature ' |
- Append(feature, new_line=False) |
- AppendRaw('Id="MainFeature"') |
- Append(TagIndent('Title="Dart Editor"', feature)) |
- # Install by default |
- Append(TagIndent('Level="1">', feature)) |
- IncreaseIndentation() |
- |
- def __exit__(self, *_): |
- DecreaseIndentation() |
- Append('</Feature>') |
- |
-def Package(): |
- package = '<Package ' |
- Append(package, new_line=False) |
- AppendRaw('InstallerVersion="301"') |
- Append(TagIndent('Compressed="yes" />', package)) |
- |
-def MediaTemplate(): |
- Append('<MediaTemplate EmbedCab="yes" />') |
- |
-def File(name, id): |
- file = '<File ' |
- Append(file, new_line=False) |
- AppendRaw('Id="FILE_%s"' % id) |
- Append(TagIndent('Source="%s"' % name, file)) |
- Append(TagIndent('KeyPath="yes" />', file)) |
- |
-def Shortcut(id, name, ref): |
- shortcut = '<Shortcut ' |
- Append(shortcut, new_line=False) |
- AppendRaw('Id="%s"' % id) |
- Append(TagIndent('Name="%s"' % name, shortcut)) |
- Append(TagIndent('Target="%s" />' % ref, shortcut)) |
- |
-def RemoveFolder(id): |
- remove = '<RemoveFolder ' |
- Append(remove, new_line=False) |
- AppendRaw('Id="%s"' % id) |
- Append(TagIndent('On="uninstall" />', remove)) |
- |
-def RegistryEntry(location): |
- registry = '<RegistryValue ' |
- Append(registry, new_line=False) |
- AppendRaw('Root="HKCU"') |
- Append(TagIndent('Key="Software\\Microsoft\\%s"' % location, registry)) |
- Append(TagIndent('Name="installed"', registry)) |
- Append(TagIndent('Type="integer"', registry)) |
- Append(TagIndent('Value="1"', registry)) |
- Append(TagIndent('KeyPath="yes" />', registry)) |
- |
- |
-def MajorUpgrade(): |
- upgrade = '<MajorUpgrade ' |
- Append(upgrade, new_line=False) |
- down_message = 'You already have a never version installed.' |
- AppendRaw('DowngradeErrorMessage="%s" />' % down_message) |
- |
- |
-# This is a very simplistic id generation. |
-# Unfortunately there is no easy way to generate good names, |
-# since there is a 72 character limit, and we have way longer |
-# paths. We don't really have an issue with files and ids across |
-# releases since we do full installs. |
-counter = 0 |
-def FileToId(name): |
- global counter |
- counter += 1 |
- return '%s' % counter |
- |
-def ListFiles(path): |
- for entry in os.listdir(path): |
- full_path = os.path.join(path, entry) |
- id = FileToId(full_path) |
- if os.path.isdir(full_path): |
- with Directory('DIR_%s' % id, entry): |
- ListFiles(full_path) |
- elif os.path.isfile(full_path): |
- # We assume 1 file per component, a File is always a KeyPath. |
- # A KeyPath on a file makes sure that we can always repair and |
- # remove that file in a consistent manner. A component |
- # can only have one child with a KeyPath. |
- with Component(id): |
- File(full_path, id) |
- |
-def ComponentRefs(): |
- for component in feature_components: |
- Append('<ComponentRef Id="%s" />' % component) |
- |
-def ExtractZipFile(zip, temp_dir): |
- print 'Extracting files' |
- f = zipfile.ZipFile(zip) |
- f.extractall(temp_dir) |
- f.close() |
- |
-def GenerateInstaller(wxs_content, options, temp_dir): |
- wxs_file = os.path.join(temp_dir, 'installer.wxs') |
- wixobj_file = os.path.join(temp_dir, 'installer.wixobj') |
- print 'Saving wxs output to: %s' % wxs_file |
- with open(wxs_file, 'w') as f: |
- f.write(wxs_content) |
- |
- candle_bin = 'candle.exe' |
- light_bin = 'light.exe' |
- if options.wix_bin: |
- candle_bin = os.path.join(options.wix_bin, 'candle.exe') |
- light_bin = os.path.join(options.wix_bin, 'light.exe') |
- print 'Calling candle on %s' % wxs_file |
- subprocess.check_call('%s %s -o %s' % (candle_bin, wxs_file, |
- wixobj_file)) |
- print 'Calling light on %s' % wixobj_file |
- subprocess.check_call('%s %s -o %s' % (light_bin, wixobj_file, |
- options.msi_location)) |
- print 'Created msi file to %s' % options.msi_location |
- |
- |
-def Main(argv): |
- if sys.platform != 'win32': |
- raise Exception("This script can only be run on windows") |
- options = GetOptions() |
- version = GetMicrosoftProductVersion(options.version) |
- with utils.TempDir('installer') as temp_dir: |
- input_location = GetInputDirectory(options, temp_dir) |
- print "Generating wix XML" |
- XmlHeader() |
- with WixAndProduct(version): |
- AppendBlankLine() |
- Package() |
- MediaTemplate() |
- AppendComment('We always do a major upgrade, at least for now') |
- MajorUpgrade() |
- |
- AppendComment('Directory structure') |
- with Directory('TARGETDIR', 'SourceDir'): |
- with Directory('ProgramFilesFolder'): |
- with Directory('RootInstallDir', 'Dart Editor'): |
- AppendComment("Add all files and directories") |
- print 'Installing files and directories in xml' |
- ListFiles(input_location) |
- AppendBlankLine() |
- AppendComment("Create shortcuts") |
- with Directory('ProgramMenuFolder'): |
- with Directory('ShortcutFolder', 'Dart Editor'): |
- with Component('shortcut'): |
- # When generating a shortcut we need an entry with |
- # a KeyPath (RegistryEntry) below - to be able to remove |
- # the shortcut again. The RemoveFolder tag is needed |
- # to clean up everything |
- Shortcut('editor_shortcut', 'Dart Editor', |
- '[RootInstallDir]DartEditor.exe') |
- RemoveFolder('RemoveShortcuts') |
- RegistryEntry('DartEditor') |
- with Feature(): |
- # We have only one feature, and it consists of all the |
- # files=components we have listed above" |
- ComponentRefs() |
- xml = GetContent() |
- if options.print_wxs: |
- print xml |
- GenerateInstaller(xml, options, temp_dir) |
- |
-if __name__ == '__main__': |
- sys.exit(Main(sys.argv)) |