Index: bootstrap/win/git_bootstrap.py |
diff --git a/bootstrap/win/git_bootstrap.py b/bootstrap/win/git_bootstrap.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0d4eabc9d506de1147e7121622f34dd6f1681447 |
--- /dev/null |
+++ b/bootstrap/win/git_bootstrap.py |
@@ -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:])) |