OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Script to create Chrome Installer archive. | 6 """Script to create Chrome Installer archive. |
7 | 7 |
8 This script is used to create an archive of all the files required for a | 8 This script is used to create an archive of all the files required for a |
9 Chrome install in appropriate directory structure. It reads chrome.release | 9 Chrome install in appropriate directory structure. It reads chrome.release |
10 file as input, creates chrome.7z archive, compresses setup.exe and | 10 file as input, creates chrome.7z archive, compresses setup.exe and |
(...skipping 24 matching lines...) Expand all Loading... |
35 COMPRESSED_FILE_EXT = ".packed.7z" # extension of patch archive file | 35 COMPRESSED_FILE_EXT = ".packed.7z" # extension of patch archive file |
36 COURGETTE_EXEC = "courgette.exe" | 36 COURGETTE_EXEC = "courgette.exe" |
37 MINI_INSTALLER_INPUT_FILE = "packed_files.txt" | 37 MINI_INSTALLER_INPUT_FILE = "packed_files.txt" |
38 PATCH_FILE_EXT = '.diff' | 38 PATCH_FILE_EXT = '.diff' |
39 SETUP_EXEC = "setup.exe" | 39 SETUP_EXEC = "setup.exe" |
40 SETUP_PATCH_FILE_PREFIX = "setup_patch" | 40 SETUP_PATCH_FILE_PREFIX = "setup_patch" |
41 TEMP_ARCHIVE_DIR = "temp_installer_archive" | 41 TEMP_ARCHIVE_DIR = "temp_installer_archive" |
42 VERSION_FILE = "VERSION" | 42 VERSION_FILE = "VERSION" |
43 | 43 |
44 | 44 |
| 45 g_archive_inputs = [] |
| 46 |
| 47 |
45 def BuildVersion(build_dir): | 48 def BuildVersion(build_dir): |
46 """Returns the full build version string constructed from information in | 49 """Returns the full build version string constructed from information in |
47 VERSION_FILE. Any segment not found in that file will default to '0'. | 50 VERSION_FILE. Any segment not found in that file will default to '0'. |
48 """ | 51 """ |
49 major = 0 | 52 major = 0 |
50 minor = 0 | 53 minor = 0 |
51 build = 0 | 54 build = 0 |
52 patch = 0 | 55 patch = 0 |
53 for line in open(os.path.join(build_dir, '../../chrome', VERSION_FILE), 'r'): | 56 for line in open(os.path.join(build_dir, '../../chrome', VERSION_FILE), 'r'): |
54 line = line.rstrip() | 57 line = line.rstrip() |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 if option.endswith('dir'): | 117 if option.endswith('dir'): |
115 continue | 118 continue |
116 | 119 |
117 dst_dir = os.path.join(staging_dir, config.get(section, option)) | 120 dst_dir = os.path.join(staging_dir, config.get(section, option)) |
118 src_paths = glob.glob(os.path.join(src_dir, option)) | 121 src_paths = glob.glob(os.path.join(src_dir, option)) |
119 if src_paths and not os.path.exists(dst_dir): | 122 if src_paths and not os.path.exists(dst_dir): |
120 os.makedirs(dst_dir) | 123 os.makedirs(dst_dir) |
121 for src_path in src_paths: | 124 for src_path in src_paths: |
122 dst_path = os.path.join(dst_dir, os.path.basename(src_path)) | 125 dst_path = os.path.join(dst_dir, os.path.basename(src_path)) |
123 if not os.path.exists(dst_path): | 126 if not os.path.exists(dst_path): |
| 127 g_archive_inputs.append(src_path) |
124 shutil.copy(src_path, dst_dir) | 128 shutil.copy(src_path, dst_dir) |
125 | 129 |
126 def GenerateDiffPatch(options, orig_file, new_file, patch_file): | 130 def GenerateDiffPatch(options, orig_file, new_file, patch_file): |
127 if (options.diff_algorithm == "COURGETTE"): | 131 if (options.diff_algorithm == "COURGETTE"): |
128 exe_file = os.path.join(options.last_chrome_installer, COURGETTE_EXEC) | 132 exe_file = os.path.join(options.last_chrome_installer, COURGETTE_EXEC) |
129 cmd = '%s -gen "%s" "%s" "%s"' % (exe_file, orig_file, new_file, patch_file) | 133 cmd = '%s -gen "%s" "%s" "%s"' % (exe_file, orig_file, new_file, patch_file) |
130 else: | 134 else: |
131 exe_file = os.path.join(options.build_dir, BSDIFF_EXEC) | 135 exe_file = os.path.join(options.build_dir, BSDIFF_EXEC) |
132 cmd = [exe_file, orig_file, new_file, patch_file,] | 136 cmd = [exe_file, orig_file, new_file, patch_file,] |
133 RunSystemCommand(cmd) | 137 RunSystemCommand(cmd) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 raise Exception("Error while running cmd: %s, exit_code: %s" % | 191 raise Exception("Error while running cmd: %s, exit_code: %s" % |
188 (cmd, exit_code)) | 192 (cmd, exit_code)) |
189 | 193 |
190 def CreateArchiveFile(options, staging_dir, current_version, prev_version): | 194 def CreateArchiveFile(options, staging_dir, current_version, prev_version): |
191 """Creates a new installer archive file after deleting any existing old file. | 195 """Creates a new installer archive file after deleting any existing old file. |
192 """ | 196 """ |
193 # First create an uncompressed archive file for the current build (chrome.7z) | 197 # First create an uncompressed archive file for the current build (chrome.7z) |
194 lzma_exec = GetLZMAExec(options.build_dir) | 198 lzma_exec = GetLZMAExec(options.build_dir) |
195 archive_file = os.path.join(options.output_dir, | 199 archive_file = os.path.join(options.output_dir, |
196 options.output_name + ARCHIVE_SUFFIX) | 200 options.output_name + ARCHIVE_SUFFIX) |
| 201 if options.depfile: |
| 202 # If a depfile was requested, do the glob of the staging dir and generate |
| 203 # a list of dependencies in .d format. We list the files that were copied |
| 204 # into the staging dir, not the files that are actually in the staging dir |
| 205 # because the ones in the staging dir will never be edited, and we want |
| 206 # to have the build be triggered when the thing-that-was-copied-there |
| 207 # changes. |
| 208 |
| 209 # Gather the list of files in the staging dir that will be zipped up. We |
| 210 # only gather this list to make sure that g_archive_inputs is complete (i.e. |
| 211 # that there's not file copies that got missed). |
| 212 staging_contents = [] |
| 213 for root, dirs, files in os.walk(os.path.join(staging_dir, CHROME_DIR)): |
| 214 for filename in files: |
| 215 staging_contents.append( |
| 216 os.path.relpath(os.path.join(root, filename), options.build_dir) |
| 217 .replace('\\', '/')) |
| 218 |
| 219 # Make sure there's an archive_input for each staging dir file. |
| 220 for staging_file in staging_contents: |
| 221 for archive_input in g_archive_inputs: |
| 222 archive_rel = (os.path.relpath(archive_input, options.build_dir) |
| 223 .replace('\\', '/')) |
| 224 if staging_file.lower().endswith('/' + archive_rel.lower()): |
| 225 break |
| 226 else: |
| 227 raise Exception('Did not find an archive input file for "%s"' % |
| 228 staging_file) |
| 229 |
| 230 # Finally, write the depfile referencing the inputs. |
| 231 with open(options.depfile, 'wb') as f: |
| 232 f.write(os.path.relpath(archive_file, options.build_dir) |
| 233 .replace('\\', '/') + ': \\\n') |
| 234 f.write(' ' + |
| 235 ' \\\n '.join(x.replace('\\', '/') for x in g_archive_inputs)) |
| 236 |
197 cmd = [lzma_exec, | 237 cmd = [lzma_exec, |
198 'a', | 238 'a', |
199 '-t7z', | 239 '-t7z', |
200 archive_file, | 240 archive_file, |
201 os.path.join(staging_dir, CHROME_DIR), | 241 os.path.join(staging_dir, CHROME_DIR), |
202 '-mx0',] | 242 '-mx0',] |
203 # There doesnt seem to be any way in 7za.exe to override existing file so | 243 # There doesnt seem to be any way in 7za.exe to override existing file so |
204 # we always delete before creating a new one. | 244 # we always delete before creating a new one. |
205 if not os.path.exists(archive_file): | 245 if not os.path.exists(archive_file): |
206 RunSystemCommand(cmd) | 246 RunSystemCommand(cmd) |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 f.write(''.join(manifest_lines)) | 383 f.write(''.join(manifest_lines)) |
344 | 384 |
345 | 385 |
346 def CopyIfChanged(src, target_dir): | 386 def CopyIfChanged(src, target_dir): |
347 """Copy specified |src| file to |target_dir|, but only write to target if | 387 """Copy specified |src| file to |target_dir|, but only write to target if |
348 the file has changed. This avoids a problem during packaging where parts of | 388 the file has changed. This avoids a problem during packaging where parts of |
349 the build have not completed and have the runtime DLL locked when we try to | 389 the build have not completed and have the runtime DLL locked when we try to |
350 copy over it. See http://crbug.com/305877 for details.""" | 390 copy over it. See http://crbug.com/305877 for details.""" |
351 assert os.path.isdir(target_dir) | 391 assert os.path.isdir(target_dir) |
352 dest = os.path.join(target_dir, os.path.basename(src)) | 392 dest = os.path.join(target_dir, os.path.basename(src)) |
| 393 g_archive_inputs.append(src) |
353 if os.path.exists(dest): | 394 if os.path.exists(dest): |
354 # We assume the files are OK to buffer fully into memory since we know | 395 # We assume the files are OK to buffer fully into memory since we know |
355 # they're only 1-2M. | 396 # they're only 1-2M. |
356 with open(src, 'rb') as fsrc: | 397 with open(src, 'rb') as fsrc: |
357 src_data = fsrc.read() | 398 src_data = fsrc.read() |
358 with open(dest, 'rb') as fdest: | 399 with open(dest, 'rb') as fdest: |
359 dest_data = fdest.read() | 400 dest_data = fdest.read() |
360 if src_data != dest_data: | 401 if src_data != dest_data: |
361 # This may still raise if we get here, but this really should almost | 402 # This may still raise if we get here, but this really should almost |
362 # never happen (it would mean that the contents of e.g. msvcr100d.dll | 403 # never happen (it would mean that the contents of e.g. msvcr100d.dll |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 setup_component_dll_globs = [ 'base.dll', | 485 setup_component_dll_globs = [ 'base.dll', |
445 'boringssl.dll', | 486 'boringssl.dll', |
446 'crcrypto.dll', | 487 'crcrypto.dll', |
447 'icui18n.dll', | 488 'icui18n.dll', |
448 'icuuc.dll', | 489 'icuuc.dll', |
449 'msvc*.dll' ] | 490 'msvc*.dll' ] |
450 for setup_component_dll_glob in setup_component_dll_globs: | 491 for setup_component_dll_glob in setup_component_dll_globs: |
451 setup_component_dlls = glob.glob(os.path.join(build_dir, | 492 setup_component_dlls = glob.glob(os.path.join(build_dir, |
452 setup_component_dll_glob)) | 493 setup_component_dll_glob)) |
453 for setup_component_dll in setup_component_dlls: | 494 for setup_component_dll in setup_component_dlls: |
| 495 g_archive_inputs.append(setup_component_dlls) |
454 shutil.copy(setup_component_dll, installer_dir) | 496 shutil.copy(setup_component_dll, installer_dir) |
455 | 497 |
456 # Stage all the component DLLs found in |build_dir| to the |version_dir| (for | 498 # Stage all the component DLLs found in |build_dir| to the |version_dir| (for |
457 # the version assembly to be able to refer to them below and make sure | 499 # the version assembly to be able to refer to them below and make sure |
458 # chrome.exe can find them at runtime). The component DLLs are considered to | 500 # chrome.exe can find them at runtime). The component DLLs are considered to |
459 # be all the DLLs which have not already been added to the |version_dir| by | 501 # be all the DLLs which have not already been added to the |version_dir| by |
460 # virtue of chrome.release. | 502 # virtue of chrome.release. |
461 build_dlls = glob.glob(os.path.join(build_dir, '*.dll')) | 503 build_dlls = glob.glob(os.path.join(build_dir, '*.dll')) |
462 staged_dll_basenames = [os.path.basename(staged_dll) for staged_dll in \ | 504 staged_dll_basenames = [os.path.basename(staged_dll) for staged_dll in \ |
463 glob.glob(os.path.join(version_dir, '*.dll'))] | 505 glob.glob(os.path.join(version_dir, '*.dll'))] |
464 component_dll_filenames = [] | 506 component_dll_filenames = [] |
465 for component_dll in [dll for dll in build_dlls if \ | 507 for component_dll in [dll for dll in build_dlls if \ |
466 os.path.basename(dll) not in staged_dll_basenames]: | 508 os.path.basename(dll) not in staged_dll_basenames]: |
467 component_dll_name = os.path.basename(component_dll) | 509 component_dll_name = os.path.basename(component_dll) |
468 # remoting_*.dll's don't belong in the archive (it doesn't depend on them | 510 # remoting_*.dll's don't belong in the archive (it doesn't depend on them |
469 # in gyp). Trying to copy them causes a build race when creating the | 511 # in gyp). Trying to copy them causes a build race when creating the |
470 # installer archive in component mode. See: crbug.com/180996 | 512 # installer archive in component mode. See: crbug.com/180996 |
471 if component_dll_name.startswith('remoting_'): | 513 if component_dll_name.startswith('remoting_'): |
472 continue | 514 continue |
473 component_dll_filenames.append(component_dll_name) | 515 component_dll_filenames.append(component_dll_name) |
| 516 g_archive_inputs.append(component_dll) |
474 shutil.copy(component_dll, version_dir) | 517 shutil.copy(component_dll, version_dir) |
475 | 518 |
476 # Augment {version}.manifest to include all component DLLs as part of the | 519 # Augment {version}.manifest to include all component DLLs as part of the |
477 # assembly it constitutes, which will allow dependents of this assembly to | 520 # assembly it constitutes, which will allow dependents of this assembly to |
478 # find these DLLs. | 521 # find these DLLs. |
479 version_assembly_dll_additions = [] | 522 version_assembly_dll_additions = [] |
480 for dll_filename in component_dll_filenames: | 523 for dll_filename in component_dll_filenames: |
481 version_assembly_dll_additions.append(" <file name='%s'/>" % dll_filename) | 524 version_assembly_dll_additions.append(" <file name='%s'/>" % dll_filename) |
482 CopyAndAugmentManifest(build_dir, version_dir, | 525 CopyAndAugmentManifest(build_dir, version_dir, |
483 '%s.manifest' % current_version, | 526 '%s.manifest' % current_version, |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 help='Diff algorithm to use when generating differential patches ' | 610 help='Diff algorithm to use when generating differential patches ' |
568 '{BSDIFF|COURGETTE}.') | 611 '{BSDIFF|COURGETTE}.') |
569 parser.add_option('-n', '--output_name', default='chrome', | 612 parser.add_option('-n', '--output_name', default='chrome', |
570 help='Name used to prefix names of generated archives.') | 613 help='Name used to prefix names of generated archives.') |
571 parser.add_option('--enable_hidpi', default='0', | 614 parser.add_option('--enable_hidpi', default='0', |
572 help='Whether to include HiDPI resource files.') | 615 help='Whether to include HiDPI resource files.') |
573 parser.add_option('--component_build', default='0', | 616 parser.add_option('--component_build', default='0', |
574 help='Whether this archive is packaging a component build. This will ' | 617 help='Whether this archive is packaging a component build. This will ' |
575 'also turn off compression of chrome.7z into chrome.packed.7z and ' | 618 'also turn off compression of chrome.7z into chrome.packed.7z and ' |
576 'helpfully delete any old chrome.packed.7z in |output_dir|.') | 619 'helpfully delete any old chrome.packed.7z in |output_dir|.') |
| 620 parser.add_option('--depfile', |
| 621 help='Generate a depfile with the given name listing the implicit inputs ' |
| 622 'to the archive process that can be used with a build system.') |
577 parser.add_option('--target_arch', default='x86', | 623 parser.add_option('--target_arch', default='x86', |
578 help='Specify the target architecture for installer - this is used ' | 624 help='Specify the target architecture for installer - this is used ' |
579 'to determine which CRT runtime files to pull and package ' | 625 'to determine which CRT runtime files to pull and package ' |
580 'with the installer archive {x86|x64}.') | 626 'with the installer archive {x86|x64}.') |
581 | 627 |
582 options, _ = parser.parse_args() | 628 options, _ = parser.parse_args() |
583 if not options.build_dir: | 629 if not options.build_dir: |
584 parser.error('You must provide a build dir.') | 630 parser.error('You must provide a build dir.') |
585 | 631 |
586 options.build_dir = os.path.normpath(options.build_dir) | 632 options.build_dir = os.path.normpath(options.build_dir) |
(...skipping 10 matching lines...) Expand all Loading... |
597 if not options.resource_file_path: | 643 if not options.resource_file_path: |
598 options.resource_file_path = os.path.join(options.build_dir, | 644 options.resource_file_path = os.path.join(options.build_dir, |
599 MINI_INSTALLER_INPUT_FILE) | 645 MINI_INSTALLER_INPUT_FILE) |
600 | 646 |
601 return options | 647 return options |
602 | 648 |
603 | 649 |
604 if '__main__' == __name__: | 650 if '__main__' == __name__: |
605 print sys.argv | 651 print sys.argv |
606 sys.exit(main(_ParseOptions())) | 652 sys.exit(main(_ParseOptions())) |
OLD | NEW |