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 def path_fixup(path): |
| 210 """Fixes path for depfile format: backslash to forward slash, and |
| 211 backslash escaping for spaces.""" |
| 212 return path.replace('\\', '/').replace(' ', '\\ ') |
| 213 |
| 214 # Gather the list of files in the staging dir that will be zipped up. We |
| 215 # only gather this list to make sure that g_archive_inputs is complete (i.e. |
| 216 # that there's not file copies that got missed). |
| 217 staging_contents = [] |
| 218 for root, dirs, files in os.walk(os.path.join(staging_dir, CHROME_DIR)): |
| 219 for filename in files: |
| 220 staging_contents.append(path_fixup(os.path.join(root, filename))) |
| 221 |
| 222 # Make sure there's an archive_input for each staging dir file. |
| 223 for staging_file in staging_contents: |
| 224 for archive_input in g_archive_inputs: |
| 225 archive_rel = path_fixup(archive_input) |
| 226 if (os.path.basename(staging_file).lower() == |
| 227 os.path.basename(archive_rel).lower()): |
| 228 break |
| 229 else: |
| 230 raise Exception('Did not find an archive input file for "%s"' % |
| 231 staging_file) |
| 232 |
| 233 # Finally, write the depfile referencing the inputs. |
| 234 with open(options.depfile, 'wb') as f: |
| 235 f.write(path_fixup(os.path.relpath(archive_file, options.build_dir)) + |
| 236 ': \\\n') |
| 237 f.write(' ' + ' \\\n '.join(path_fixup(x) for x in g_archive_inputs)) |
| 238 |
197 cmd = [lzma_exec, | 239 cmd = [lzma_exec, |
198 'a', | 240 'a', |
199 '-t7z', | 241 '-t7z', |
200 archive_file, | 242 archive_file, |
201 os.path.join(staging_dir, CHROME_DIR), | 243 os.path.join(staging_dir, CHROME_DIR), |
202 '-mx0',] | 244 '-mx0',] |
203 # There doesnt seem to be any way in 7za.exe to override existing file so | 245 # There doesnt seem to be any way in 7za.exe to override existing file so |
204 # we always delete before creating a new one. | 246 # we always delete before creating a new one. |
205 if not os.path.exists(archive_file): | 247 if not os.path.exists(archive_file): |
206 RunSystemCommand(cmd) | 248 RunSystemCommand(cmd) |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 f.write(''.join(manifest_lines)) | 385 f.write(''.join(manifest_lines)) |
344 | 386 |
345 | 387 |
346 def CopyIfChanged(src, target_dir): | 388 def CopyIfChanged(src, target_dir): |
347 """Copy specified |src| file to |target_dir|, but only write to target if | 389 """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 | 390 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 | 391 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.""" | 392 copy over it. See http://crbug.com/305877 for details.""" |
351 assert os.path.isdir(target_dir) | 393 assert os.path.isdir(target_dir) |
352 dest = os.path.join(target_dir, os.path.basename(src)) | 394 dest = os.path.join(target_dir, os.path.basename(src)) |
| 395 g_archive_inputs.append(src) |
353 if os.path.exists(dest): | 396 if os.path.exists(dest): |
354 # We assume the files are OK to buffer fully into memory since we know | 397 # We assume the files are OK to buffer fully into memory since we know |
355 # they're only 1-2M. | 398 # they're only 1-2M. |
356 with open(src, 'rb') as fsrc: | 399 with open(src, 'rb') as fsrc: |
357 src_data = fsrc.read() | 400 src_data = fsrc.read() |
358 with open(dest, 'rb') as fdest: | 401 with open(dest, 'rb') as fdest: |
359 dest_data = fdest.read() | 402 dest_data = fdest.read() |
360 if src_data != dest_data: | 403 if src_data != dest_data: |
361 # This may still raise if we get here, but this really should almost | 404 # 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 | 405 # 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', | 487 setup_component_dll_globs = [ 'base.dll', |
445 'boringssl.dll', | 488 'boringssl.dll', |
446 'crcrypto.dll', | 489 'crcrypto.dll', |
447 'icui18n.dll', | 490 'icui18n.dll', |
448 'icuuc.dll', | 491 'icuuc.dll', |
449 'msvc*.dll' ] | 492 'msvc*.dll' ] |
450 for setup_component_dll_glob in setup_component_dll_globs: | 493 for setup_component_dll_glob in setup_component_dll_globs: |
451 setup_component_dlls = glob.glob(os.path.join(build_dir, | 494 setup_component_dlls = glob.glob(os.path.join(build_dir, |
452 setup_component_dll_glob)) | 495 setup_component_dll_glob)) |
453 for setup_component_dll in setup_component_dlls: | 496 for setup_component_dll in setup_component_dlls: |
| 497 g_archive_inputs.append(setup_component_dll) |
454 shutil.copy(setup_component_dll, installer_dir) | 498 shutil.copy(setup_component_dll, installer_dir) |
455 | 499 |
456 # Stage all the component DLLs found in |build_dir| to the |version_dir| (for | 500 # 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 | 501 # 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 | 502 # 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 | 503 # be all the DLLs which have not already been added to the |version_dir| by |
460 # virtue of chrome.release. | 504 # virtue of chrome.release. |
461 build_dlls = glob.glob(os.path.join(build_dir, '*.dll')) | 505 build_dlls = glob.glob(os.path.join(build_dir, '*.dll')) |
462 staged_dll_basenames = [os.path.basename(staged_dll) for staged_dll in \ | 506 staged_dll_basenames = [os.path.basename(staged_dll) for staged_dll in \ |
463 glob.glob(os.path.join(version_dir, '*.dll'))] | 507 glob.glob(os.path.join(version_dir, '*.dll'))] |
464 component_dll_filenames = [] | 508 component_dll_filenames = [] |
465 for component_dll in [dll for dll in build_dlls if \ | 509 for component_dll in [dll for dll in build_dlls if \ |
466 os.path.basename(dll) not in staged_dll_basenames]: | 510 os.path.basename(dll) not in staged_dll_basenames]: |
467 component_dll_name = os.path.basename(component_dll) | 511 component_dll_name = os.path.basename(component_dll) |
468 # remoting_*.dll's don't belong in the archive (it doesn't depend on them | 512 # 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 | 513 # in gyp). Trying to copy them causes a build race when creating the |
470 # installer archive in component mode. See: crbug.com/180996 | 514 # installer archive in component mode. See: crbug.com/180996 |
471 if component_dll_name.startswith('remoting_'): | 515 if component_dll_name.startswith('remoting_'): |
472 continue | 516 continue |
473 component_dll_filenames.append(component_dll_name) | 517 component_dll_filenames.append(component_dll_name) |
| 518 g_archive_inputs.append(component_dll) |
474 shutil.copy(component_dll, version_dir) | 519 shutil.copy(component_dll, version_dir) |
475 | 520 |
476 # Augment {version}.manifest to include all component DLLs as part of the | 521 # Augment {version}.manifest to include all component DLLs as part of the |
477 # assembly it constitutes, which will allow dependents of this assembly to | 522 # assembly it constitutes, which will allow dependents of this assembly to |
478 # find these DLLs. | 523 # find these DLLs. |
479 version_assembly_dll_additions = [] | 524 version_assembly_dll_additions = [] |
480 for dll_filename in component_dll_filenames: | 525 for dll_filename in component_dll_filenames: |
481 version_assembly_dll_additions.append(" <file name='%s'/>" % dll_filename) | 526 version_assembly_dll_additions.append(" <file name='%s'/>" % dll_filename) |
482 CopyAndAugmentManifest(build_dir, version_dir, | 527 CopyAndAugmentManifest(build_dir, version_dir, |
483 '%s.manifest' % current_version, | 528 '%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 ' | 612 help='Diff algorithm to use when generating differential patches ' |
568 '{BSDIFF|COURGETTE}.') | 613 '{BSDIFF|COURGETTE}.') |
569 parser.add_option('-n', '--output_name', default='chrome', | 614 parser.add_option('-n', '--output_name', default='chrome', |
570 help='Name used to prefix names of generated archives.') | 615 help='Name used to prefix names of generated archives.') |
571 parser.add_option('--enable_hidpi', default='0', | 616 parser.add_option('--enable_hidpi', default='0', |
572 help='Whether to include HiDPI resource files.') | 617 help='Whether to include HiDPI resource files.') |
573 parser.add_option('--component_build', default='0', | 618 parser.add_option('--component_build', default='0', |
574 help='Whether this archive is packaging a component build. This will ' | 619 help='Whether this archive is packaging a component build. This will ' |
575 'also turn off compression of chrome.7z into chrome.packed.7z and ' | 620 'also turn off compression of chrome.7z into chrome.packed.7z and ' |
576 'helpfully delete any old chrome.packed.7z in |output_dir|.') | 621 'helpfully delete any old chrome.packed.7z in |output_dir|.') |
| 622 parser.add_option('--depfile', |
| 623 help='Generate a depfile with the given name listing the implicit inputs ' |
| 624 'to the archive process that can be used with a build system.') |
577 parser.add_option('--target_arch', default='x86', | 625 parser.add_option('--target_arch', default='x86', |
578 help='Specify the target architecture for installer - this is used ' | 626 help='Specify the target architecture for installer - this is used ' |
579 'to determine which CRT runtime files to pull and package ' | 627 'to determine which CRT runtime files to pull and package ' |
580 'with the installer archive {x86|x64}.') | 628 'with the installer archive {x86|x64}.') |
581 | 629 |
582 options, _ = parser.parse_args() | 630 options, _ = parser.parse_args() |
583 if not options.build_dir: | 631 if not options.build_dir: |
584 parser.error('You must provide a build dir.') | 632 parser.error('You must provide a build dir.') |
585 | 633 |
586 options.build_dir = os.path.normpath(options.build_dir) | 634 options.build_dir = os.path.normpath(options.build_dir) |
(...skipping 10 matching lines...) Expand all Loading... |
597 if not options.resource_file_path: | 645 if not options.resource_file_path: |
598 options.resource_file_path = os.path.join(options.build_dir, | 646 options.resource_file_path = os.path.join(options.build_dir, |
599 MINI_INSTALLER_INPUT_FILE) | 647 MINI_INSTALLER_INPUT_FILE) |
600 | 648 |
601 return options | 649 return options |
602 | 650 |
603 | 651 |
604 if '__main__' == __name__: | 652 if '__main__' == __name__: |
605 print sys.argv | 653 print sys.argv |
606 sys.exit(main(_ParseOptions())) | 654 sys.exit(main(_ParseOptions())) |
OLD | NEW |