| #!/usr/bin/env python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Script to create Chrome Installer archive. |
| |
| This script is used to create an archive of all the files required for a |
| Chrome install in appropriate directory structure. It reads chrome.release |
| file as input, creates chrome.7z archive, compresses setup.exe and |
| generates packed_files.txt for mini_installer project. |
| |
| """ |
| |
| import ConfigParser |
| import fnmatch |
| import glob |
| import optparse |
| import os |
| import re |
| import shutil |
| import subprocess |
| import sys |
| |
| |
| ARCHIVE_DIR = "installer_archive" |
| |
| # suffix to uncompresed full archive file, appended to options.output_name |
| ARCHIVE_SUFFIX = ".7z" |
| BSDIFF_EXEC = "bsdiff.exe" |
| CHROME_DIR = "Chrome-bin" |
| CHROME_PATCH_FILE_SUFFIX = "_patch" # prefixed by options.output_name |
| |
| # compressed full archive suffix, will be prefixed by options.output_name |
| COMPRESSED_ARCHIVE_SUFFIX = ".packed.7z" |
| |
| COMPRESSED_FILE_EXT = ".packed.7z" # extension of patch archive file |
| COURGETTE_EXEC = "courgette.exe" |
| MINI_INSTALLER_INPUT_FILE = "packed_files.txt" |
| PATCH_FILE_EXT = '.diff' |
| SETUP_EXEC = "setup.exe" |
| SETUP_PATCH_FILE_PREFIX = "setup_patch" |
| TEMP_ARCHIVE_DIR = "temp_installer_archive" |
| VERSION_FILE = "VERSION" |
| |
| |
| g_archive_inputs = [] |
| |
| |
| def BuildVersion(build_dir): |
| """Returns the full build version string constructed from information in |
| VERSION_FILE. Any segment not found in that file will default to '0'. |
| """ |
| major = 0 |
| minor = 0 |
| build = 0 |
| patch = 0 |
| for line in open(os.path.join(build_dir, '../../chrome', VERSION_FILE), 'r'): |
| line = line.rstrip() |
| if line.startswith('MAJOR='): |
| major = line[6:] |
| elif line.startswith('MINOR='): |
| minor = line[6:] |
| elif line.startswith('BUILD='): |
| build = line[6:] |
| elif line.startswith('PATCH='): |
| patch = line[6:] |
| return '%s.%s.%s.%s' % (major, minor, build, patch) |
| |
| |
| def CompressUsingLZMA(build_dir, compressed_file, input_file, verbose): |
| lzma_exec = GetLZMAExec(build_dir) |
| cmd = [lzma_exec, |
| 'a', '-t7z', |
| # Flags equivalent to -mx9 (ultra) but with the bcj2 turned on (exe |
| # pre-filter). This results in a ~2.3MB decrease in installer size on |
| # a 24MB installer. |
| # Additionally, these settings reflect a 7zip 4.42 and up change in |
| # the definition of -mx9, increasting the dicionary size moving to |
| # 26bit = 64MB. This results in an additional ~3.5MB decrease. |
| # Older 7zip versions can support these settings, as these changes |
| # rely on existing functionality in the lzma format. |
| '-m0=BCJ2', |
| '-m1=LZMA:d27:fb128', |
| '-m2=LZMA:d22:fb128:mf=bt2', |
| '-m3=LZMA:d22:fb128:mf=bt2', |
| '-mb0:1', |
| '-mb0s1:2', |
| '-mb0s2:3', |
| os.path.abspath(compressed_file), |
| os.path.abspath(input_file),] |
| if os.path.exists(compressed_file): |
| os.remove(compressed_file) |
| RunSystemCommand(cmd, verbose) |
| |
| |
| def CopyAllFilesToStagingDir(config, distribution, staging_dir, build_dir, |
| enable_hidpi): |
| """Copies the files required for installer archive. |
| Copies all common files required for various distributions of Chromium and |
| also files for the specific Chromium build specified by distribution. |
| """ |
| CopySectionFilesToStagingDir(config, 'GENERAL', staging_dir, build_dir) |
| if distribution: |
| if len(distribution) > 1 and distribution[0] == '_': |
| distribution = distribution[1:] |
| |
| distribution = distribution.upper() |
| if config.has_section(distribution): |
| CopySectionFilesToStagingDir(config, distribution, |
| staging_dir, build_dir) |
| if enable_hidpi == '1': |
| CopySectionFilesToStagingDir(config, 'HIDPI', staging_dir, build_dir) |
| |
| |
| def CopySectionFilesToStagingDir(config, section, staging_dir, src_dir): |
| """Copies installer archive files specified in section from src_dir to |
| staging_dir. This method reads section from config and copies all the |
| files specified from src_dir to staging dir. |
| """ |
| for option in config.options(section): |
| if option.endswith('dir'): |
| continue |
| |
| dst_dir = os.path.join(staging_dir, config.get(section, option)) |
| dst_dir = dst_dir.replace('\\', os.sep) |
| src_paths = glob.glob(os.path.join(src_dir, option)) |
| if src_paths and not os.path.exists(dst_dir): |
| os.makedirs(dst_dir) |
| for src_path in src_paths: |
| dst_path = os.path.join(dst_dir, os.path.basename(src_path)) |
| if not os.path.exists(dst_path): |
| g_archive_inputs.append(src_path) |
| shutil.copy(src_path, dst_dir) |
| |
| def GenerateDiffPatch(options, orig_file, new_file, patch_file): |
| if (options.diff_algorithm == "COURGETTE"): |
| exe_file = os.path.join(options.last_chrome_installer, COURGETTE_EXEC) |
| cmd = '%s -gen "%s" "%s" "%s"' % (exe_file, orig_file, new_file, patch_file) |
| else: |
| exe_file = os.path.join(options.build_dir, BSDIFF_EXEC) |
| cmd = [exe_file, orig_file, new_file, patch_file,] |
| RunSystemCommand(cmd, options.verbose) |
| |
| def GetLZMAExec(build_dir): |
| if sys.platform == 'win32': |
| lzma_exec = os.path.join(build_dir, "..", "..", "third_party", |
| "lzma_sdk", "Executable", "7za.exe") |
| else: |
| lzma_exec = '7zr' # Use system 7zr. |
| return lzma_exec |
| |
| def GetPrevVersion(build_dir, temp_dir, last_chrome_installer, output_name): |
| if not last_chrome_installer: |
| return '' |
| |
| lzma_exec = GetLZMAExec(build_dir) |
| prev_archive_file = os.path.join(last_chrome_installer, |
| output_name + ARCHIVE_SUFFIX) |
| cmd = [lzma_exec, |
| 'x', |
| '-o"%s"' % temp_dir, |
| prev_archive_file, |
| 'Chrome-bin/*/chrome.dll',] |
| RunSystemCommand(cmd, options.verbose) |
| dll_path = glob.glob(os.path.join(temp_dir, 'Chrome-bin', '*', 'chrome.dll')) |
| return os.path.split(os.path.split(dll_path[0])[0])[1] |
| |
| def MakeStagingDirectory(staging_dir): |
| """Creates a staging path for installer archive. If directory exists already, |
| deletes the existing directory. |
| """ |
| file_path = os.path.join(staging_dir, TEMP_ARCHIVE_DIR) |
| if os.path.exists(file_path): |
| shutil.rmtree(file_path) |
| os.makedirs(file_path) |
| return file_path |
| |
| def Readconfig(input_file, current_version): |
| """Reads config information from input file after setting default value of |
| global variabes. |
| """ |
| variables = {} |
| variables['ChromeDir'] = CHROME_DIR |
| variables['VersionDir'] = os.path.join(variables['ChromeDir'], |
| current_version) |
| config = ConfigParser.SafeConfigParser(variables) |
| config.read(input_file) |
| return config |
| |
| def RunSystemCommand(cmd, verbose): |
| """Runs |cmd|, prints the |cmd| and its output if |verbose|; otherwise |
| captures its output and only emits it on failure. |
| """ |
| if verbose: |
| print 'Running', cmd |
| |
| try: |
| # Run |cmd|, redirecting stderr to stdout in order for captured errors to be |
| # inline with corresponding stdout. |
| output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) |
| if verbose: |
| print output |
| except subprocess.CalledProcessError as e: |
| raise Exception("Error while running cmd: %s\n" |
| "Exit code: %s\n" |
| "Command output:\n%s" % |
| (e.cmd, e.returncode, e.output)) |
| |
| def CreateArchiveFile(options, staging_dir, current_version, prev_version): |
| """Creates a new installer archive file after deleting any existing old file. |
| """ |
| # First create an uncompressed archive file for the current build (chrome.7z) |
| lzma_exec = GetLZMAExec(options.build_dir) |
| archive_file = os.path.join(options.output_dir, |
| options.output_name + ARCHIVE_SUFFIX) |
| if options.depfile: |
| # If a depfile was requested, do the glob of the staging dir and generate |
| # a list of dependencies in .d format. We list the files that were copied |
| # into the staging dir, not the files that are actually in the staging dir |
| # because the ones in the staging dir will never be edited, and we want |
| # to have the build be triggered when the thing-that-was-copied-there |
| # changes. |
| |
| def path_fixup(path): |
| """Fixes path for depfile format: backslash to forward slash, and |
| backslash escaping for spaces.""" |
| return path.replace('\\', '/').replace(' ', '\\ ') |
| |
| # Gather the list of files in the staging dir that will be zipped up. We |
| # only gather this list to make sure that g_archive_inputs is complete (i.e. |
| # that there's not file copies that got missed). |
| staging_contents = [] |
| for root, dirs, files in os.walk(os.path.join(staging_dir, CHROME_DIR)): |
| for filename in files: |
| staging_contents.append(path_fixup(os.path.join(root, filename))) |
| |
| # Make sure there's an archive_input for each staging dir file. |
| for staging_file in staging_contents: |
| for archive_input in g_archive_inputs: |
| archive_rel = path_fixup(archive_input) |
| if (os.path.basename(staging_file).lower() == |
| os.path.basename(archive_rel).lower()): |
| break |
| else: |
| raise Exception('Did not find an archive input file for "%s"' % |
| staging_file) |
| |
| # Finally, write the depfile referencing the inputs. |
| with open(options.depfile, 'wb') as f: |
| f.write(path_fixup(os.path.relpath(archive_file, options.build_dir)) + |
| ': \\\n') |
| f.write(' ' + ' \\\n '.join(path_fixup(x) for x in g_archive_inputs)) |
| |
| # It is important to use abspath to create the path to the directory because |
| # if you use a relative path without any .. sequences then 7za.exe uses the |
| # entire relative path as part of the file paths in the archive. If you have |
| # a .. sequence or an absolute path then only the last directory is stored as |
| # part of the file paths in the archive, which is what we want. |
| cmd = [lzma_exec, |
| 'a', |
| '-t7z', |
| archive_file, |
| os.path.abspath(os.path.join(staging_dir, CHROME_DIR)), |
| '-mx0',] |
| # There doesnt seem to be any way in 7za.exe to override existing file so |
| # we always delete before creating a new one. |
| if not os.path.exists(archive_file): |
| RunSystemCommand(cmd, options.verbose) |
| elif options.skip_rebuild_archive != "true": |
| os.remove(archive_file) |
| RunSystemCommand(cmd, options.verbose) |
| |
| # Do not compress the archive when skip_archive_compression is specified. |
| if options.skip_archive_compression: |
| compressed_file = os.path.join( |
| options.output_dir, options.output_name + COMPRESSED_ARCHIVE_SUFFIX) |
| if os.path.exists(compressed_file): |
| os.remove(compressed_file) |
| return os.path.basename(archive_file) |
| |
| # If we are generating a patch, run bsdiff against previous build and |
| # compress the resulting patch file. If this is not a patch just compress the |
| # uncompressed archive file. |
| patch_name_prefix = options.output_name + CHROME_PATCH_FILE_SUFFIX |
| if options.last_chrome_installer: |
| prev_archive_file = os.path.join(options.last_chrome_installer, |
| options.output_name + ARCHIVE_SUFFIX) |
| patch_file = os.path.join(options.build_dir, patch_name_prefix + |
| PATCH_FILE_EXT) |
| GenerateDiffPatch(options, prev_archive_file, archive_file, patch_file) |
| compressed_archive_file = patch_name_prefix + '_' + \ |
| current_version + '_from_' + prev_version + \ |
| COMPRESSED_FILE_EXT |
| orig_file = patch_file |
| else: |
| compressed_archive_file = options.output_name + COMPRESSED_ARCHIVE_SUFFIX |
| orig_file = archive_file |
| |
| compressed_archive_file_path = os.path.join(options.output_dir, |
| compressed_archive_file) |
| CompressUsingLZMA(options.build_dir, compressed_archive_file_path, orig_file, |
| options.verbose) |
| |
| return compressed_archive_file |
| |
| |
| def PrepareSetupExec(options, current_version, prev_version): |
| """Prepares setup.exe for bundling in mini_installer based on options.""" |
| if options.setup_exe_format == "FULL": |
| setup_file = SETUP_EXEC |
| elif options.setup_exe_format == "DIFF": |
| if not options.last_chrome_installer: |
| raise Exception( |
| "To use DIFF for setup.exe, --last_chrome_installer is needed.") |
| prev_setup_file = os.path.join(options.last_chrome_installer, SETUP_EXEC) |
| new_setup_file = os.path.join(options.build_dir, SETUP_EXEC) |
| patch_file = os.path.join(options.build_dir, SETUP_PATCH_FILE_PREFIX + |
| PATCH_FILE_EXT) |
| GenerateDiffPatch(options, prev_setup_file, new_setup_file, patch_file) |
| setup_file = SETUP_PATCH_FILE_PREFIX + '_' + current_version + \ |
| '_from_' + prev_version + COMPRESSED_FILE_EXT |
| setup_file_path = os.path.join(options.build_dir, setup_file) |
| CompressUsingLZMA(options.build_dir, setup_file_path, patch_file, |
| options.verbose) |
| else: |
| # Use makecab.py instead of makecab.exe so that this works when building |
| # on non-Windows hosts too. |
| makecab_py = os.path.join(os.path.dirname(__file__), 'makecab.py') |
| cmd = [sys.executable, makecab_py, |
| '/D', 'CompressionType=LZX', |
| '/V1', |
| '/L', options.output_dir, |
| os.path.join(options.build_dir, SETUP_EXEC),] |
| RunSystemCommand(cmd, options.verbose) |
| setup_file = SETUP_EXEC[:-1] + "_" |
| return setup_file |
| |
| |
| _RESOURCE_FILE_HEADER = """\ |
| // This file is automatically generated by create_installer_archive.py. |
| // It contains the resource entries that are going to be linked inside |
| // mini_installer.exe. For each file to be linked there should be two |
| // lines: |
| // - The first line contains the output filename (without path) and the |
| // type of the resource ('BN' - not compressed , 'BL' - LZ compressed, |
| // 'B7' - LZMA compressed) |
| // - The second line contains the path to the input file. Uses '/' to |
| // separate path components. |
| """ |
| |
| |
| def CreateResourceInputFile( |
| output_dir, setup_format, archive_file, setup_file, resource_file_path, |
| component_build, staging_dir, current_version): |
| """Creates resource input file (packed_files.txt) for mini_installer project. |
| |
| This method checks the format of setup.exe being used and according sets |
| its resource type. |
| """ |
| setup_resource_type = "BL" |
| if (setup_format == "FULL"): |
| setup_resource_type = "BN" |
| elif (setup_format == "DIFF"): |
| setup_resource_type = "B7" |
| |
| # An array of (file, type, path) tuples of the files to be included. |
| resources = [] |
| resources.append((setup_file, setup_resource_type, |
| os.path.join(output_dir, setup_file))) |
| resources.append((archive_file, 'B7', |
| os.path.join(output_dir, archive_file))) |
| # Include all files needed to run setup.exe (these are copied into the |
| # 'Installer' dir by DoComponentBuildTasks). |
| if component_build: |
| installer_dir = os.path.join(staging_dir, CHROME_DIR, current_version, |
| 'Installer') |
| for file in os.listdir(installer_dir): |
| resources.append((file, 'BN', os.path.join(installer_dir, file))) |
| |
| with open(resource_file_path, 'w') as f: |
| f.write(_RESOURCE_FILE_HEADER) |
| for (file, type, path) in resources: |
| f.write('\n%s %s\n "%s"\n' % (file, type, path.replace("\\","/"))) |
| |
| |
| # Reads |manifest_name| from |build_dir| and writes |manifest_name| to |
| # |output_dir| with the same content plus |inserted_string| added just before |
| # |insert_before|. |
| def CopyAndAugmentManifest(build_dir, output_dir, manifest_name, |
| inserted_string, insert_before): |
| with open(os.path.join(build_dir, manifest_name), 'r') as f: |
| manifest_lines = f.readlines() |
| |
| insert_line = -1 |
| insert_pos = -1 |
| for i in xrange(len(manifest_lines)): |
| insert_pos = manifest_lines[i].find(insert_before) |
| if insert_pos != -1: |
| insert_line = i |
| break |
| if insert_line == -1: |
| raise ValueError('Could not find {0} in the manifest:\n{1}'.format( |
| insert_before, ''.join(manifest_lines))) |
| old = manifest_lines[insert_line] |
| manifest_lines[insert_line] = (old[:insert_pos] + '\n' + inserted_string + |
| '\n' + old[insert_pos:]) |
| |
| with open(os.path.join(output_dir, manifest_name), 'w') as f : |
| f.write(''.join(manifest_lines)) |
| |
| |
| def CopyIfChanged(src, target_dir): |
| """Copy specified |src| file to |target_dir|, but only write to target if |
| the file has changed. This avoids a problem during packaging where parts of |
| the build have not completed and have the runtime DLL locked when we try to |
| copy over it. See http://crbug.com/305877 for details.""" |
| assert os.path.isdir(target_dir) |
| dest = os.path.join(target_dir, os.path.basename(src)) |
| g_archive_inputs.append(src) |
| if os.path.exists(dest): |
| # We assume the files are OK to buffer fully into memory since we know |
| # they're only 1-2M. |
| with open(src, 'rb') as fsrc: |
| src_data = fsrc.read() |
| with open(dest, 'rb') as fdest: |
| dest_data = fdest.read() |
| if src_data != dest_data: |
| # This may still raise if we get here, but this really should almost |
| # never happen (it would mean that the contents of e.g. msvcr100d.dll |
| # had been changed). |
| shutil.copyfile(src, dest) |
| else: |
| shutil.copyfile(src, dest) |
| |
| |
| # Taken and modified from: |
| # third_party\blink\tools\blinkpy\web_tests\port\factory.py |
| def _read_configuration_from_gn(build_dir): |
| """Return the configuration to used based on args.gn, if possible.""" |
| path = os.path.join(build_dir, 'args.gn') |
| if not os.path.exists(path): |
| path = os.path.join(build_dir, 'toolchain.ninja') |
| if not os.path.exists(path): |
| # This does not appear to be a GN-based build directory, so we don't |
| # know how to interpret it. |
| return None |
| |
| # toolchain.ninja exists, but args.gn does not; this can happen when |
| # `gn gen` is run with no --args. |
| return 'Debug' |
| |
| args = open(path).read() |
| for l in args.splitlines(): |
| # See the original of this function and then gn documentation for why this |
| # regular expression is correct: |
| # https://chromium.googlesource.com/chromium/src/+/master/tools/gn/docs/reference.md#GN-build-language-grammar |
| m = re.match('^\s*is_debug\s*=\s*false(\s*$|\s*#.*$)', l) |
| if m: |
| return 'Release' |
| |
| # if is_debug is set to anything other than false, or if it |
| # does not exist at all, we should use the default value (True). |
| return 'Debug' |
| |
| |
| def ParseDLLsFromDeps(build_dir, runtime_deps_file): |
| """Parses the runtime_deps file and returns the set of DLLs in it, relative |
| to build_dir.""" |
| build_dlls = set() |
| args = open(runtime_deps_file).read() |
| for l in args.splitlines(): |
| if os.path.splitext(l)[1] == ".dll": |
| build_dlls.add(os.path.join(build_dir, l)) |
| return build_dlls |
| |
| # Copies component build DLLs and generates required config files and manifests |
| # in order for chrome.exe and setup.exe to be able to find those DLLs at |
| # run-time. |
| # This is meant for developer builds only and should never be used to package |
| # an official build. |
| def DoComponentBuildTasks(staging_dir, build_dir, target_arch, |
| setup_runtime_deps, chrome_runtime_deps, |
| current_version): |
| # Get the required directories for the upcoming operations. |
| chrome_dir = os.path.join(staging_dir, CHROME_DIR) |
| version_dir = os.path.join(chrome_dir, current_version) |
| installer_dir = os.path.join(version_dir, 'Installer') |
| # |installer_dir| is technically only created post-install, but we need it |
| # now to add setup.exe's config and manifest to the archive. |
| if not os.path.exists(installer_dir): |
| os.mkdir(installer_dir) |
| |
| setup_component_dlls = ParseDLLsFromDeps(build_dir, setup_runtime_deps) |
| |
| for setup_component_dll in setup_component_dlls: |
| g_archive_inputs.append(setup_component_dll) |
| shutil.copy(setup_component_dll, installer_dir) |
| |
| # Stage all the component DLLs to the |version_dir| (for |
| # the version assembly to be able to refer to them below and make sure |
| # chrome.exe can find them at runtime), except the ones that are already |
| # staged (i.e. non-component DLLs). |
| build_dlls = ParseDLLsFromDeps(build_dir, chrome_runtime_deps) |
| # Generate a list of relative dll paths that have already been staged into the |
| # version directory (i.e., non-component DLLs). |
| staged_dlls = [os.path.normcase(os.path.relpath(os.path.join(dir, file), |
| version_dir)) \ |
| for dir, _, files in os.walk(version_dir) \ |
| for file in files if fnmatch.fnmatch(file, '*.dll')] |
| component_dll_filenames = [] |
| for component_dll in [dll for dll in build_dlls if \ |
| os.path.normcase(os.path.relpath(dll, build_dir)) \ |
| not in staged_dlls]: |
| component_dll_name = os.path.basename(component_dll) |
| component_dll_filenames.append(component_dll_name) |
| g_archive_inputs.append(component_dll) |
| shutil.copy(component_dll, version_dir) |
| |
| # Augment {version}.manifest to include all component DLLs as part of the |
| # assembly it constitutes, which will allow dependents of this assembly to |
| # find these DLLs. |
| version_assembly_dll_additions = [] |
| for dll_filename in component_dll_filenames: |
| version_assembly_dll_additions.append(" <file name='%s'/>" % dll_filename) |
| CopyAndAugmentManifest(build_dir, version_dir, |
| '%s.manifest' % current_version, |
| '\n'.join(version_assembly_dll_additions), |
| '</assembly>') |
| |
| |
| def main(options): |
| """Main method that reads input file, creates archive file and writes |
| resource input file. |
| """ |
| current_version = BuildVersion(options.build_dir) |
| |
| config = Readconfig(options.input_file, current_version) |
| |
| staging_dir = MakeStagingDirectory(options.staging_dir) |
| |
| prev_version = GetPrevVersion(options.build_dir, staging_dir, |
| options.last_chrome_installer, |
| options.output_name) |
| |
| # Copy the files from the build dir. |
| CopyAllFilesToStagingDir(config, options.distribution, |
| staging_dir, options.build_dir, |
| options.enable_hidpi) |
| |
| if options.component_build == '1': |
| DoComponentBuildTasks(staging_dir, options.build_dir, |
| options.target_arch, options.setup_runtime_deps, |
| options.chrome_runtime_deps, current_version) |
| |
| version_numbers = current_version.split('.') |
| current_build_number = version_numbers[2] + '.' + version_numbers[3] |
| prev_build_number = '' |
| if prev_version: |
| version_numbers = prev_version.split('.') |
| prev_build_number = version_numbers[2] + '.' + version_numbers[3] |
| |
| # Name of the archive file built (for example - chrome.7z or |
| # patch-<old_version>-<new_version>.7z or patch-<new_version>.7z |
| archive_file = CreateArchiveFile(options, staging_dir, |
| current_build_number, prev_build_number) |
| |
| setup_file = PrepareSetupExec(options, |
| current_build_number, prev_build_number) |
| |
| CreateResourceInputFile(options.output_dir, options.setup_exe_format, |
| archive_file, setup_file, options.resource_file_path, |
| options.component_build == '1', staging_dir, |
| current_version) |
| |
| def _ParseOptions(): |
| parser = optparse.OptionParser() |
| parser.add_option('-i', '--input_file', |
| help='Input file describing which files to archive.') |
| parser.add_option('-b', '--build_dir', |
| help='Build directory. The paths in input_file are relative to this.') |
| parser.add_option('--staging_dir', |
| help='Staging directory where intermediate files and directories ' |
| 'will be created') |
| parser.add_option('-o', '--output_dir', |
| help='The output directory where the archives will be written. ' |
| 'Defaults to the build_dir.') |
| parser.add_option('--resource_file_path', |
| help='The path where the resource file will be output. ' |
| 'Defaults to %s in the build directory.' % |
| MINI_INSTALLER_INPUT_FILE) |
| parser.add_option('-d', '--distribution', |
| help='Name of Chromium Distribution. Optional.') |
| parser.add_option('-s', '--skip_rebuild_archive', |
| default="False", help='Skip re-building Chrome.7z archive if it exists.') |
| parser.add_option('-l', '--last_chrome_installer', |
| help='Generate differential installer. The value of this parameter ' |
| 'specifies the directory that contains base versions of ' |
| 'setup.exe, courgette.exe (if --diff_algorithm is COURGETTE) ' |
| '& chrome.7z.') |
| parser.add_option('-f', '--setup_exe_format', default='COMPRESSED', |
| help='How setup.exe should be included {COMPRESSED|DIFF|FULL}.') |
| parser.add_option('-a', '--diff_algorithm', default='BSDIFF', |
| help='Diff algorithm to use when generating differential patches ' |
| '{BSDIFF|COURGETTE}.') |
| parser.add_option('-n', '--output_name', default='chrome', |
| help='Name used to prefix names of generated archives.') |
| parser.add_option('--enable_hidpi', default='0', |
| help='Whether to include HiDPI resource files.') |
| parser.add_option('--component_build', default='0', |
| help='Whether this archive is packaging a component build.') |
| parser.add_option('--skip_archive_compression', |
| action='store_true', default=False, |
| help='This will turn off compression of chrome.7z into chrome.packed.7z ' |
| 'and helpfully delete any old chrome.packed.7z in |output_dir|.') |
| parser.add_option('--depfile', |
| help='Generate a depfile with the given name listing the implicit inputs ' |
| 'to the archive process that can be used with a build system.') |
| parser.add_option('--chrome_runtime_deps', |
| help='A file listing runtime dependencies. This will be used to get a ' |
| 'list of DLLs to archive in a component build.') |
| parser.add_option('--setup_runtime_deps', |
| help='A file listing runtime dependencies for setup.exe. This will be ' |
| 'used to get a list of DLLs to archive in a component build.') |
| parser.add_option('--target_arch', default='x86', |
| help='Specify the target architecture for installer - this is used ' |
| 'to determine which CRT runtime files to pull and package ' |
| 'with the installer archive {x86|x64}.') |
| parser.add_option('-v', '--verbose', action='store_true', dest='verbose', |
| default=False) |
| |
| options, _ = parser.parse_args() |
| if not options.build_dir: |
| parser.error('You must provide a build dir.') |
| |
| options.build_dir = os.path.normpath(options.build_dir) |
| |
| if not options.staging_dir: |
| parser.error('You must provide a staging dir.') |
| |
| if not options.input_file: |
| parser.error('You must provide an input file') |
| |
| is_component_build = options.component_build == '1' |
| if is_component_build and not options.chrome_runtime_deps: |
| parser.error("chrome_runtime_deps must be specified for a component build") |
| if is_component_build and not options.setup_runtime_deps: |
| parser.error("setup_runtime_deps must be specified for a component build") |
| |
| if not options.output_dir: |
| options.output_dir = options.build_dir |
| |
| if not options.resource_file_path: |
| options.resource_file_path = os.path.join(options.build_dir, |
| MINI_INSTALLER_INPUT_FILE) |
| |
| return options |
| |
| |
| if '__main__' == __name__: |
| options = _ParseOptions() |
| if options.verbose: |
| print sys.argv |
| sys.exit(main(options)) |