Index: remoting/tools/zip2msi.py |
diff --git a/remoting/tools/zip2msi.py b/remoting/tools/zip2msi.py |
index e474430af8ef51fb9eee883aaa96db7f6a6caa59..bbe8c8abd111443d72436a9e09818d3e9c0c0bf2 100755 |
--- a/remoting/tools/zip2msi.py |
+++ b/remoting/tools/zip2msi.py |
@@ -3,20 +3,20 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
-""" |
- Generates .msi from a .zip archive or an uppacked directory. The structure of |
- the input archive or directory should look like this: |
+"""Generates .msi from a .zip archive or an unpacked directory. |
+ |
+The structure of the input archive or directory should look like this: |
+- archive.zip |
+- archive |
+- parameters.json |
- The name of the archive and the top level directory in the archive must match. |
- When an unpacked directory is used as the input "archive.zip/archive" should |
- be passed via the command line. |
+The name of the archive and the top level directory in the archive must match. |
+When an unpacked directory is used as the input "archive.zip/archive" should |
+be passed via the command line. |
- 'parameters.json' specifies the parameters to be passed to candle/light and |
- must have the following structure: |
+'parameters.json' specifies the parameters to be passed to candle/light and |
+must have the following structure: |
{ |
"defines": { "name": "value" }, |
@@ -24,14 +24,25 @@ |
"switches": [ '-nologo' ], |
"source": "chromoting.wxs", |
"bind_path": "files", |
+ "sign": [ ... ], |
"candle": { ... }, |
"light": { ... } |
} |
- "source" specifies the name of the input .wxs relative to |
- "archive.zip/archive". |
- "bind_path" specifies the path where to look for binary files referenced by |
- .wxs relative to "archive.zip/archive". |
+"source" specifies the name of the input .wxs relative to |
+ "archive.zip/archive". |
+"bind_path" specifies the path where to look for binary files referenced by |
+ .wxs relative to "archive.zip/archive". |
+ |
+This script is used for both building Chromoting Host installation during |
+Chromuim build and for signing Chromoting Host installation later. There are two |
+copies of this script because of that: |
+ |
+ - one in Chromium tree at src/remoting/tools/zip2msi.py. |
+ - another one next to the signing scripts. |
+ |
+The copies of the script can be out of sync so make sure that a newer version is |
+compatible with the older ones when updating the script. |
""" |
import copy |
@@ -43,28 +54,35 @@ import subprocess |
import sys |
import zipfile |
-def extractZip(source, dest): |
- """ Extracts |source| ZIP archive to |dest| folder returning |True| if |
- successful.""" |
+ |
+def UnpackZip(target, source): |
+ """Unpacks |source| archive to |target| directory.""" |
+ target = os.path.normpath(target) |
archive = zipfile.ZipFile(source, 'r') |
for f in archive.namelist(): |
- target = os.path.normpath(os.path.join(dest, f)) |
+ target_file = os.path.normpath(os.path.join(target, f)) |
# Sanity check to make sure .zip uses relative paths. |
- if os.path.commonprefix([target, dest]) != dest: |
+ if os.path.commonprefix([target_file, target]) != target: |
print "Failed to unpack '%s': '%s' is not under '%s'" % ( |
- source, target, dest) |
- return False |
+ source, target_file, target) |
+ return 1 |
# Create intermediate directories. |
- target_dir = os.path.dirname(target) |
+ target_dir = os.path.dirname(target_file) |
if not os.path.exists(target_dir): |
os.makedirs(target_dir) |
- archive.extract(f, dest) |
- return True |
+ archive.extract(f, target) |
+ return 0 |
+ |
+ |
+def Merge(left, right): |
+ """Merges two values. |
+ |
+ Raises: |
+ TypeError: |left| and |right| cannot be merged. |
-def merge(left, right): |
- """ Merges to values. The result is: |
+ Returns: |
- if both |left| and |right| are dictionaries, they are merged recursively. |
- if both |left| and |right| are lists, the result is a list containing |
elements from both lists. |
@@ -77,29 +95,30 @@ def merge(left, right): |
retval = copy.copy(left) |
for key, value in right.iteritems(): |
if key in retval: |
- retval[key] = merge(retval[key], value) |
+ retval[key] = Merge(retval[key], value) |
else: |
retval[key] = value |
return retval |
else: |
- raise TypeError("Error: merging a dictionary and non-dictionary value") |
+ raise TypeError('Error: merging a dictionary and non-dictionary value') |
elif isinstance(left, list): |
if isinstance(right, list): |
return left + right |
else: |
- raise TypeError("Error: merging a list and non-list value") |
+ raise TypeError('Error: merging a list and non-list value') |
else: |
if isinstance(right, dict): |
- raise TypeError("Error: merging a dictionary and non-dictionary value") |
+ raise TypeError('Error: merging a dictionary and non-dictionary value') |
elif isinstance(right, list): |
- raise TypeError("Error: merging a dictionary and non-dictionary value") |
+ raise TypeError('Error: merging a dictionary and non-dictionary value') |
else: |
return right |
quote_matcher_regex = re.compile(r'\s|"') |
quote_replacer_regex = re.compile(r'(\\*)"') |
-def quoteArgument(arg): |
+ |
+def QuoteArgument(arg): |
"""Escapes a Windows command-line argument. |
So that the Win32 CommandLineToArgv function will turn the escaped result back |
@@ -128,15 +147,16 @@ def quoteArgument(arg): |
else: |
return arg |
-def generateCommandLine(tool, source, dest, parameters): |
+ |
+def GenerateCommandLine(tool, source, dest, parameters): |
"""Generates the command line for |tool|.""" |
# Merge/apply tool-specific parameters |
params = copy.copy(parameters) |
if tool in parameters: |
- params = merge(params, params[tool]) |
+ params = Merge(params, params[tool]) |
wix_path = os.path.normpath(params.get('wix_path', '')) |
- switches = [ os.path.join(wix_path, tool), '-nologo' ] |
+ switches = [os.path.join(wix_path, tool), '-nologo'] |
# Append the list of defines and extensions to the command line switches. |
for name, value in params.get('defines', {}).iteritems(): |
@@ -152,13 +172,13 @@ def generateCommandLine(tool, source, dest, parameters): |
switches += ('-out', dest, source) |
# Generate the actual command line |
- #return ' '.join(map(quoteArgument, switches)) |
+ #return ' '.join(map(QuoteArgument, switches)) |
return switches |
-def run(args): |
- """ Constructs a quoted command line from the passed |args| list and runs it. |
- Prints the exit code and output of the command if an error occurs.""" |
- command = ' '.join(map(quoteArgument, args)) |
+ |
+def Run(args): |
+ """Runs a command interpreting the passed |args| as a command line.""" |
+ command = ' '.join(map(QuoteArgument, args)) |
popen = subprocess.Popen( |
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
out, _ = popen.communicate() |
@@ -169,68 +189,70 @@ def run(args): |
print '%s returned %d' % (args[0], popen.returncode) |
return popen.returncode |
-def main(): |
- usage = "Usage: zip2msi [options] <input.zip> <output.msi>" |
- parser = OptionParser(usage=usage) |
- parser.add_option('--intermediate_dir', dest='intermediate_dir') |
- parser.add_option('--wix_path', dest='wix_path') |
- options, args = parser.parse_args() |
- if len(args) != 2: |
- parser.error("two positional arguments expected") |
- parameters = dict(options.__dict__) |
- parameters['basename'] = os.path.splitext(os.path.basename(args[0]))[0] |
- if not options.intermediate_dir: |
- parameters['intermediate_dir'] = os.path.normpath('.') |
+def GenerateMsi(target, source, parameters): |
+ """Generates .msi from the installation files prepared by Chromium build.""" |
+ parameters['basename'] = os.path.splitext(os.path.basename(source))[0] |
# The script can handle both forms of input a directory with unpacked files or |
# a ZIP archive with the same files. In the latter case the archive should be |
# unpacked to the intermediate directory. |
- intermediate_dir = os.path.normpath(parameters['intermediate_dir']) |
source_dir = None |
- if os.path.isdir(args[0]): |
+ if os.path.isdir(source): |
# Just use unpacked files from the supplied directory. |
- source_dir = args[0] |
+ source_dir = source |
else: |
# Unpack .zip |
- if not extractZip(args[0], intermediate_dir): |
- return 1 |
+ rc = UnpackZip(parameters['intermediate_dir'], source) |
+ if rc != 0: |
+ return rc |
source_dir = '%(intermediate_dir)s\\%(basename)s' % parameters |
# Read parameters from 'parameters.json'. |
f = open(os.path.join(source_dir, 'parameters.json')) |
- parameters = merge(parameters, json.load(f)) |
+ parameters = Merge(json.load(f), parameters) |
f.close() |
if 'source' not in parameters: |
- print "The source .wxs is not specified" |
+ print 'The source .wxs is not specified' |
return 1 |
if 'bind_path' not in parameters: |
- print "The binding path is not specified" |
+ print 'The binding path is not specified' |
return 1 |
- dest = args[1] |
- source = os.path.join(source_dir, parameters['source']) |
+ wxs = os.path.join(source_dir, parameters['source']) |
# Add the binding path to the light-specific parameters. |
bind_path = os.path.join(source_dir, parameters['bind_path']) |
- parameters = merge(parameters, {'light': {'switches': ['-b', bind_path]}}) |
+ parameters = Merge(parameters, {'light': {'switches': ['-b', bind_path]}}) |
# Run candle and light to generate the installation. |
wixobj = '%(intermediate_dir)s\\%(basename)s.wixobj' % parameters |
- args = generateCommandLine('candle', source, wixobj, parameters) |
- rc = run(args) |
+ args = GenerateCommandLine('candle', wxs, wixobj, parameters) |
+ rc = Run(args) |
if rc: |
return rc |
- args = generateCommandLine('light', wixobj, dest, parameters) |
- rc = run(args) |
+ args = GenerateCommandLine('light', wixobj, target, parameters) |
+ rc = Run(args) |
if rc: |
return rc |
return 0 |
-if __name__ == "__main__": |
+ |
+def main(): |
+ usage = 'Usage: zip2msi [options] <input.zip> <output.msi>' |
+ parser = OptionParser(usage=usage) |
+ parser.add_option('--intermediate_dir', dest='intermediate_dir', default='.') |
+ parser.add_option('--wix_path', dest='wix_path', default='.') |
+ options, args = parser.parse_args() |
+ if len(args) != 2: |
+ parser.error('two positional arguments expected') |
+ |
+ return GenerateMsi(args[1], args[0], dict(options.__dict__)) |
+ |
+if __name__ == '__main__': |
sys.exit(main()) |