Add git_bootstrap.py, which bootstraps git on Windows using CIPD
It's backwards compatible with the logic in win_tools.bat and is intended to eventually replace it. BUG=635421, 629679 Review-Url: https://codereview.chromium.org/2254843004changes/00/373500/1
parent
439170a2e7
commit
6d0a5acdc4
@ -0,0 +1,199 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
ROOT_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..'))
|
||||
|
||||
DEVNULL = open(os.devnull, 'w')
|
||||
|
||||
|
||||
def _check_call(argv, **kwargs):
|
||||
"""Wrapper for subprocess.check_call that adds logging."""
|
||||
logging.info('running %r', argv)
|
||||
subprocess.check_call(argv, **kwargs)
|
||||
|
||||
|
||||
def get_target_git_version():
|
||||
"""Returns git version that should be installed."""
|
||||
if os.path.exists(os.path.join(ROOT_DIR, '.git_bleeding_edge')):
|
||||
git_version_file = 'git_version_bleeding_edge.txt'
|
||||
else:
|
||||
git_version_file = 'git_version.txt'
|
||||
with open(os.path.join(THIS_DIR, git_version_file)) as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
def clean_up_old_git_installations(git_directory):
|
||||
"""Removes git installations other than |git_directory|."""
|
||||
for entry in fnmatch.filter(os.listdir(ROOT_DIR), 'git-*_bin'):
|
||||
full_entry = os.path.join(ROOT_DIR, entry)
|
||||
if full_entry != git_directory:
|
||||
logging.info('Cleaning up old git installation %r', entry)
|
||||
shutil.rmtree(full_entry, ignore_errors=True)
|
||||
|
||||
|
||||
def bootstrap_cipd(cipd_directory):
|
||||
"""Bootstraps CIPD client into |cipd_directory|."""
|
||||
_check_call([
|
||||
sys.executable,
|
||||
os.path.join(ROOT_DIR, 'recipe_modules', 'cipd', 'resources',
|
||||
'bootstrap.py'),
|
||||
'--platform', 'windows-amd64',
|
||||
'--dest-directory', cipd_directory
|
||||
])
|
||||
|
||||
|
||||
def cipd_install(args, dest_directory, package, version):
|
||||
"""Installs CIPD |package| at |version| into |dest_directory|."""
|
||||
manifest_file = tempfile.mktemp()
|
||||
try:
|
||||
with open(manifest_file, 'w') as f:
|
||||
f.write('%s %s\n' % (package, version))
|
||||
|
||||
cipd_args = [
|
||||
args.cipd_client,
|
||||
'ensure',
|
||||
'-list', manifest_file,
|
||||
'-root', dest_directory,
|
||||
]
|
||||
if args.cipd_cache_directory:
|
||||
cipd_args.extend(['-cache-dir', args.cipd_cache_directory])
|
||||
if args.verbose:
|
||||
cipd_args.append('-verbose')
|
||||
_check_call(cipd_args)
|
||||
finally:
|
||||
os.remove(manifest_file)
|
||||
|
||||
|
||||
def need_to_install_git(args, git_directory):
|
||||
"""Returns True if git needs to be installed."""
|
||||
if args.force:
|
||||
return True
|
||||
git_exe_path = os.path.join(git_directory, 'bin', 'git.exe')
|
||||
if not os.path.exists(git_exe_path):
|
||||
return True
|
||||
if subprocess.call(
|
||||
[git_exe_path, '--version'],
|
||||
stdout=DEVNULL, stderr=DEVNULL) != 0:
|
||||
return True
|
||||
for script in ('git.bat', 'gitk.bat', 'ssh.bat', 'ssh-keygen.bat',
|
||||
'git-bash'):
|
||||
full_path = os.path.join(ROOT_DIR, script)
|
||||
if not os.path.exists(full_path):
|
||||
return True
|
||||
with open(full_path) as f:
|
||||
if os.path.relpath(git_directory, ROOT_DIR) not in f.read():
|
||||
return True
|
||||
if not os.path.exists(os.path.join(
|
||||
git_directory, 'etc', 'profile.d', 'python.sh')):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def install_git(args, git_version, git_directory):
|
||||
"""Installs |git_version| into |git_directory|."""
|
||||
if not args.cipd_client:
|
||||
bootstrap_cipd(ROOT_DIR)
|
||||
args.cipd_client = os.path.join(ROOT_DIR, 'cipd')
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
cipd_install(args,
|
||||
temp_dir,
|
||||
'infra/depot_tools/git_installer/windows-amd64',
|
||||
'v' + git_version.replace('.', '_'))
|
||||
|
||||
if not os.path.exists(git_directory):
|
||||
os.makedirs(git_directory)
|
||||
|
||||
# 7-zip has weird expectations for command-line syntax. Pass it as a string
|
||||
# to avoid subprocess module quoting breaking it. Also double-escape
|
||||
# backslashes in paths.
|
||||
_check_call(' '.join([
|
||||
os.path.join(temp_dir, 'git-installer.exe'),
|
||||
'-y',
|
||||
'-InstallPath="%s"' % git_directory.replace('\\', '\\\\'),
|
||||
'-Directory="%s"' % git_directory.replace('\\', '\\\\'),
|
||||
]))
|
||||
finally:
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
with open(os.path.join(THIS_DIR, 'git.template.bat')) as f:
|
||||
git_template = f.read()
|
||||
git_template = git_template.replace(
|
||||
'GIT_BIN_DIR', os.path.relpath(git_directory, ROOT_DIR))
|
||||
scripts = (
|
||||
('git.bat', 'cmd\\git.exe'),
|
||||
('gitk.bat', 'cmd\\gitk.exe'),
|
||||
('ssh.bat', 'usr\\bin\\ssh.exe'),
|
||||
('ssh-keygen.bat', 'usr\\bin\\ssh-keygen.exe'),
|
||||
)
|
||||
for script in scripts:
|
||||
with open(os.path.join(ROOT_DIR, script[0]), 'w') as f:
|
||||
f.write(git_template.replace('GIT_PROGRAM', script[1]))
|
||||
with open(os.path.join(THIS_DIR, 'git-bash.template.sh')) as f:
|
||||
git_bash_template = f.read()
|
||||
with open(os.path.join(ROOT_DIR, 'git-bash'), 'w') as f:
|
||||
f.write(git_bash_template.replace(
|
||||
'GIT_BIN_DIR', os.path.relpath(git_directory, ROOT_DIR)))
|
||||
shutil.copyfile(
|
||||
os.path.join(THIS_DIR, 'profile.d.python.sh'),
|
||||
os.path.join(git_directory, 'etc', 'profile.d', 'python.sh'))
|
||||
|
||||
git_bat_path = os.path.join(ROOT_DIR, 'git.bat')
|
||||
_check_call([git_bat_path, 'config', '--system', 'core.autocrlf', 'false'])
|
||||
_check_call([git_bat_path, 'config', '--system', 'core.filemode', 'false'])
|
||||
_check_call([git_bat_path, 'config', '--system', 'core.preloadindex', 'true'])
|
||||
_check_call([git_bat_path, 'config', '--system', 'core.fscache', 'true'])
|
||||
|
||||
|
||||
def sync_git_help_files(git_directory):
|
||||
"""Updates depot_tools help files inside |git_directory|."""
|
||||
# TODO(phajdan.jr): consider replacing the call with python code.
|
||||
_check_call([
|
||||
'xcopy', '/i', '/q', '/d', '/y',
|
||||
os.path.join(ROOT_DIR, 'man', 'html', '*'),
|
||||
os.path.join(git_directory, 'mingw64', 'share', 'doc', 'git-doc')],
|
||||
stdout=DEVNULL)
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--cipd-client', help='Path to CIPD client binary.')
|
||||
parser.add_argument('--cipd-cache-directory',
|
||||
help='Path to CIPD cache directory.')
|
||||
parser.add_argument('--force', action='store_true',
|
||||
help='Always re-install git.')
|
||||
parser.add_argument('--verbose', action='store_true')
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if os.environ.get('WIN_TOOLS_FORCE') == '1':
|
||||
args.force = True
|
||||
|
||||
logging.basicConfig(level=logging.INFO if args.verbose else logging.WARN)
|
||||
|
||||
git_version = get_target_git_version()
|
||||
git_directory = os.path.join(ROOT_DIR, 'git-%s-64_bin' % git_version)
|
||||
|
||||
clean_up_old_git_installations(git_directory)
|
||||
|
||||
if need_to_install_git(args, git_directory):
|
||||
install_git(args, git_version, git_directory)
|
||||
|
||||
sync_git_help_files(git_directory)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
Loading…
Reference in New Issue