1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
#!/usr/bin/env python
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This script builds the credential provider installer that is used to install
# all required components of the Google Credential Provider for Windows. The
# installer is a 7-zip self extracting executable file that wraps three main
# parts:
#
# - the Credential Provider COM DLL
# - a DLL that contains Windows EventLog message formatting
# - a setup exe that performs action required during install and uninstall
#
# In this description "installer" refers to the self extracting executable that
# wraps all the parts, whereas "setup" refers to an exe inside the installer
# that runs specific actions at install and uninstall time.
#
# When run, the installer extracts the wrapped files into a new empty
# directory under %TEMP%. The setup exe is then run to register the COM
# objects, install the message format dll, and properly register the credential
# provider with Windows. Once installation completes, the new directory
# containing the extracted files is automatically deleted.
#
# The installer can be run multiple times on the same machine. On an already
# working computer this is essentially a noop. On a damaged computer the files
# will be overwritten and the parts registered, so can be used to correct
# problems.
#
# Running a new version of the installer will replace the existing install with
# a newer one. It is not required to first uninstall the old version.
# Installation of the newer version will attempt to delete older versions if
# possible.
#
# The installer is not needed for uninstall and may be removed after initial
# install. To uninstall the Google Credential Provider for Windows, run the
# setup exe with the command line argument: /uninstall
"""Creates the GCPW self extracting installer. This script is not run manually,
it is called when building the //credential_provider:gcp_installer GN target.
All paths can be absolute or relative to $root_build_dir.
"""
import argparse
import os
import shutil
import subprocess
import sys
def GetLZMAExec(src_path):
"""Gets the path to the 7zip compression command line tool.
Args:
src_path: Full path to the source root
Returns:
The executable command to run the 7zip compressor.
"""
executable = '7zr'
if sys.platform == 'win32':
executable += '.exe'
return os.path.join(src_path, 'third_party', 'lzma_sdk', 'bin',
'host_platform', executable)
def GetCmdLine(command, sz_fn, gcp_7z_fn):
"""Builds the command line for the given archive.
Args:
command: 7Zip command such as 'u', 'rn'..
sz_fn: The executable command to run 7zip.
gcp_7z_fn: 7zip file for the archive.
Returns:
Returns the command line for the provided command and 7zip archive. Command
needs to be one of the supported 7zip commands.
"""
return [
sz_fn, # Path to 7z executable.
command,
# The follow options are equivalent to -mx9 with bcj2 turned on.
# Because //third_party/lzma_sdk is only partial copy of the ful sdk
# it does not support all forms of compression. Make sure to use
# compression that is compatible. These same options are used when
# building the chrome install compressed files.
'-m0=BCJ2',
'-m1=LZMA:d27:fb128',
'-m2=LZMA:d22:fb128:mf=bt2',
'-m3=LZMA:d22:fb128:mf=bt2',
'-mb0:1',
'-mb0s1:2',
'-mb0s2:3',
# Full path to archive.
gcp_7z_fn,
]
def main():
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('src_path', help='Path to the source root')
parser.add_argument('cp_path',
help='Path to the credential provider directory')
parser.add_argument('root_build_path', help='$root_build_dir GN variable')
parser.add_argument('target_gen_path', help='$target_gen_dir GN variable')
args = parser.parse_args()
# Make sure all arguments are converted to absolute paths for use below.
args.src_path = os.path.abspath(args.src_path)
args.cp_path = os.path.abspath(args.cp_path)
args.root_build_path = os.path.abspath(args.root_build_path)
args.target_gen_path = os.path.abspath(args.target_gen_path)
if not os.path.isdir(args.cp_path):
parser.error('Invalid cp_path: "%s"' % args.cp_path)
if not os.path.isdir(args.src_path):
parser.error('Invalid src_path: "%s"' % args.src_path)
# Absolute path to gcp installer.
gcp_installer_fn = os.path.join(args.root_build_path, 'gcp_installer.exe')
gcp_7z_fn = os.path.join(args.root_build_path, 'gcp.7z')
sz_fn = GetLZMAExec(args.src_path)
sfx_fn = os.path.join(args.root_build_path, 'gcp_sfx.exe')
# Build the command line for updating files in the GCP 7z archive.
u_cmd = GetCmdLine('u', sz_fn, gcp_7z_fn)
# 7Zip CLI doesn't provide a direct way of adding files into a custom
# folder. As suggested by the developer of 7Zip the method is to rename
# a file with a specific subfolder to achieve the same.
# https://sourceforge.net/p/sevenzip/discussion/45798/thread/5856d980/
# For instance, if there is a file called "a.txt" in the parent folder of
# archive, when it is renamed with "f\a.txt", the "a.txt" file is actually
# placed in a folder called "f".
rn_cmd = GetCmdLine('rn', sz_fn, gcp_7z_fn)
# Builds the command line for deleting files in the archive.
d_cmd = GetCmdLine('d', sz_fn, gcp_7z_fn)
# Because of the way that 7zS2.sfx determine what program to run after
# extraction, only gcp_setup.exe should be placed in the root of the archive.
# Other "executable" type files (bat, cmd, exe, inf, msi, html, htm) should
# be located only in subfolders. That's why all the files initially added in
# the top folder. Then the ones need to move to subfolders are renamed. 7z
# doesn't have a method to achieve the same directly.
# Add the credential provider dll, credential provider extension and setup
# programs to the archive. If the files added to the archive are changed,
# make sure to update the kFilenames array in setup_lib.cc.
try:
gcpw_log_file = 'gcpw_archive_log_file'
if os.path.exists(gcpw_log_file):
os.remove(gcpw_log_file)
# Redirecting output of 7zip and copy commands to a file and only printing
# if any of the subprocess commands fail.
with open(gcpw_log_file, "w+") as output_file:
os.chdir(args.root_build_path)
subprocess.check_call(d_cmd + ['*'], stdout=output_file)
subprocess.check_call(u_cmd + ['gaia1_0.dll'], stdout=output_file)
subprocess.check_call(u_cmd + ['gcp_setup.exe'], stdout=output_file)
subprocess.check_call(u_cmd + ['gcp_eventlog_provider.dll'],
stdout=output_file)
subprocess.check_call(u_cmd + ['gcpw_extension.exe'], stdout=output_file)
# Move the executable into a subfolder as there needs to be only one
# executable in the parent folder.
subprocess.check_call(rn_cmd +
[
'gcpw_extension.exe',
os.path.join('extension', 'gcpw_extension.exe')
],
stdout=output_file)
except subprocess.CalledProcessError as e:
print(e.output)
with open(gcpw_log_file, "r") as output_file:
print(output_file.read())
raise e
# Combine the SFX module with the archive to make a self extracting
# executable.
with open(gcp_installer_fn, 'wb') as output:
with open (sfx_fn, 'rb') as input:
shutil.copyfileobj(input, output)
with open (gcp_7z_fn, 'rb') as input:
shutil.copyfileobj(input, output)
return 0
if __name__ == '__main__':
sys.exit(main())
|