| 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())
|
|
|
|
|