mirror of
https://chromium.googlesource.com/chromium/tools/depot_tools.git
synced 2026-01-11 10:41:31 +00:00
Prefer direct git installation for Windows
Bug: b/360206460 Change-Id: I7068b201b0b976ac619db16ff16d5ffbe5aea362 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5884196 Reviewed-by: Allen Li <ayatane@chromium.org> Reviewed-by: Josip Sokcevic <sokcevic@chromium.org> Commit-Queue: Anne Redulla <aredulla@google.com>
This commit is contained in:
@@ -40,8 +40,7 @@ class Template(
|
|||||||
collections.namedtuple('Template', (
|
collections.namedtuple('Template', (
|
||||||
'PYTHON3_BIN_RELDIR',
|
'PYTHON3_BIN_RELDIR',
|
||||||
'PYTHON3_BIN_RELDIR_UNIX',
|
'PYTHON3_BIN_RELDIR_UNIX',
|
||||||
'GIT_BIN_RELDIR',
|
'GIT_BIN_ABSDIR',
|
||||||
'GIT_BIN_RELDIR_UNIX',
|
|
||||||
'GIT_PROGRAM',
|
'GIT_PROGRAM',
|
||||||
))):
|
))):
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -222,10 +221,10 @@ def _safe_rmtree(path):
|
|||||||
def clean_up_old_installations(skip_dir):
|
def clean_up_old_installations(skip_dir):
|
||||||
"""Removes Python installations other than |skip_dir|.
|
"""Removes Python installations other than |skip_dir|.
|
||||||
|
|
||||||
This includes an "in-use" check against the "python.exe" in a given directory
|
This includes an "in-use" check against the "python.exe" in a given
|
||||||
to avoid removing Python executables that are currently ruinning. We need
|
directory to avoid removing Python executables that are currently running.
|
||||||
this because our Python bootstrap may be run after (and by) other software
|
We need this because our Python bootstrap may be run after (and by) other
|
||||||
that is using the bootstrapped Python!
|
software that is using the bootstrapped Python!
|
||||||
"""
|
"""
|
||||||
root_contents = os.listdir(ROOT_DIR)
|
root_contents = os.listdir(ROOT_DIR)
|
||||||
for f in ('win_tools-*_bin', 'python27*_bin', 'git-*_bin',
|
for f in ('win_tools-*_bin', 'python27*_bin', 'git-*_bin',
|
||||||
@@ -246,6 +245,56 @@ def clean_up_old_installations(skip_dir):
|
|||||||
GIT_POSTPROCESS_VERSION = '2'
|
GIT_POSTPROCESS_VERSION = '2'
|
||||||
|
|
||||||
|
|
||||||
|
def _within_depot_tools(path):
|
||||||
|
"""Returns whether the given path is within depot_tools."""
|
||||||
|
return os.path.commonpath([os.path.abspath(path), ROOT_DIR]) == ROOT_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def _traverse_to_git_root(abspath):
|
||||||
|
"""Traverses up the path to the closest "git" directory (case-insensitive).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The path to the directory with name "git" (case-insensitive), if it
|
||||||
|
exists as an ancestor; otherwise, None.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* "C:\Program Files\Git\cmd" -> "C:\Program Files\Git"
|
||||||
|
* "C:\Program Files\Git\mingw64\bin" -> "C:\Program Files\Git"
|
||||||
|
"""
|
||||||
|
head, tail = os.path.split(abspath)
|
||||||
|
while tail:
|
||||||
|
if tail.lower() == 'git':
|
||||||
|
return os.path.join(head, tail)
|
||||||
|
head, tail = os.path.split(head)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def search_win_git_directory():
|
||||||
|
"""Searches for a git directory outside of depot_tools.
|
||||||
|
|
||||||
|
As depot_tools will soon stop bundling Git for Windows, this function logs
|
||||||
|
a warning if git has not yet been directly installed.
|
||||||
|
"""
|
||||||
|
# Look for the git command in PATH outside of depot_tools.
|
||||||
|
for p in os.environ.get('PATH', '').split(os.pathsep):
|
||||||
|
if _within_depot_tools(p):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for cmd in ('git.exe', 'git.bat'):
|
||||||
|
if os.path.isfile(os.path.join(p, cmd)):
|
||||||
|
git_root = _traverse_to_git_root(p)
|
||||||
|
if git_root:
|
||||||
|
return git_root
|
||||||
|
|
||||||
|
# Log deprecation warning.
|
||||||
|
logging.warning(
|
||||||
|
'depot_tools will soon stop bundling Git for Windows.\n'
|
||||||
|
'To prepare for this change, please install Git directly. See\n'
|
||||||
|
'https://chromium.googlesource.com/chromium/src/+/main/docs/windows_build_instructions.md#Install-git\n'
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def git_get_mingw_dir(git_directory):
|
def git_get_mingw_dir(git_directory):
|
||||||
"""Returns (str) The "mingw" directory in a Git installation, or None."""
|
"""Returns (str) The "mingw" directory in a Git installation, or None."""
|
||||||
for candidate in ('mingw64', 'mingw32'):
|
for candidate in ('mingw64', 'mingw32'):
|
||||||
@@ -255,17 +304,19 @@ def git_get_mingw_dir(git_directory):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def git_postprocess(template, git_directory):
|
def git_postprocess(template, bootstrap_git_dir, add_docs):
|
||||||
# Update depot_tools files for "git help <command>"
|
if add_docs:
|
||||||
mingw_dir = git_get_mingw_dir(git_directory)
|
# Update depot_tools files for "git help <command>".
|
||||||
if mingw_dir:
|
mingw_dir = git_get_mingw_dir(template.GIT_BIN_ABSDIR)
|
||||||
docsrc = os.path.join(ROOT_DIR, 'man', 'html')
|
if mingw_dir:
|
||||||
git_docs_dir = os.path.join(mingw_dir, 'share', 'doc', 'git-doc')
|
docsrc = os.path.join(ROOT_DIR, 'man', 'html')
|
||||||
for name in os.listdir(docsrc):
|
git_docs_dir = os.path.join(mingw_dir, 'share', 'doc', 'git-doc')
|
||||||
maybe_copy(os.path.join(docsrc, name),
|
for name in os.listdir(docsrc):
|
||||||
os.path.join(git_docs_dir, name))
|
maybe_copy(os.path.join(docsrc, name),
|
||||||
else:
|
os.path.join(git_docs_dir, name))
|
||||||
logging.info('Could not find mingw directory for %r.', git_directory)
|
else:
|
||||||
|
logging.info('Could not find mingw directory for %r.',
|
||||||
|
template.GIT_BIN_ABSDIR)
|
||||||
|
|
||||||
# Create Git templates and configure its base layout.
|
# Create Git templates and configure its base layout.
|
||||||
for stub_name, relpath in WIN_GIT_STUBS.items():
|
for stub_name, relpath in WIN_GIT_STUBS.items():
|
||||||
@@ -289,7 +340,8 @@ def git_postprocess(template, git_directory):
|
|||||||
_check_call(
|
_check_call(
|
||||||
[git_bat_path, 'config', '--system', 'protocol.version', '2'])
|
[git_bat_path, 'config', '--system', 'protocol.version', '2'])
|
||||||
|
|
||||||
call_if_outdated(os.path.join(git_directory, '.git_postprocess'),
|
os.makedirs(bootstrap_git_dir, exist_ok=True)
|
||||||
|
call_if_outdated(os.path.join(bootstrap_git_dir, '.git_postprocess'),
|
||||||
GIT_POSTPROCESS_VERSION, configure_git_system)
|
GIT_POSTPROCESS_VERSION, configure_git_system)
|
||||||
|
|
||||||
|
|
||||||
@@ -306,9 +358,7 @@ def main(argv):
|
|||||||
template = Template.empty()._replace(
|
template = Template.empty()._replace(
|
||||||
PYTHON3_BIN_RELDIR=os.path.join(args.bootstrap_name, 'python3', 'bin'),
|
PYTHON3_BIN_RELDIR=os.path.join(args.bootstrap_name, 'python3', 'bin'),
|
||||||
PYTHON3_BIN_RELDIR_UNIX=posixpath.join(args.bootstrap_name, 'python3',
|
PYTHON3_BIN_RELDIR_UNIX=posixpath.join(args.bootstrap_name, 'python3',
|
||||||
'bin'),
|
'bin'))
|
||||||
GIT_BIN_RELDIR=os.path.join(args.bootstrap_name, 'git'),
|
|
||||||
GIT_BIN_RELDIR_UNIX=posixpath.join(args.bootstrap_name, 'git'))
|
|
||||||
|
|
||||||
bootstrap_dir = os.path.join(ROOT_DIR, args.bootstrap_name)
|
bootstrap_dir = os.path.join(ROOT_DIR, args.bootstrap_name)
|
||||||
|
|
||||||
@@ -316,7 +366,16 @@ def main(argv):
|
|||||||
clean_up_old_installations(bootstrap_dir)
|
clean_up_old_installations(bootstrap_dir)
|
||||||
|
|
||||||
if IS_WIN:
|
if IS_WIN:
|
||||||
git_postprocess(template, os.path.join(bootstrap_dir, 'git'))
|
bootstrap_git_dir = os.path.join(bootstrap_dir, 'git')
|
||||||
|
# Avoid messing with system git docs.
|
||||||
|
add_docs = False
|
||||||
|
git_dir = search_win_git_directory()
|
||||||
|
if not git_dir:
|
||||||
|
# git not found in PATH - fall back to depot_tools bundled git.
|
||||||
|
git_dir = bootstrap_git_dir
|
||||||
|
add_docs = True
|
||||||
|
template = template._replace(GIT_BIN_ABSDIR=git_dir)
|
||||||
|
git_postprocess(template, bootstrap_git_dir, add_docs)
|
||||||
templates = [
|
templates = [
|
||||||
('git-bash.template.sh', 'git-bash', ROOT_DIR),
|
('git-bash.template.sh', 'git-bash', ROOT_DIR),
|
||||||
('python3.bat', 'python3.bat', ROOT_DIR),
|
('python3.bat', 'python3.bat', ROOT_DIR),
|
||||||
|
|||||||
@@ -5,8 +5,13 @@ UNIX_BASE=`cygpath "$WIN_BASE"`
|
|||||||
export PATH="$PATH:$UNIX_BASE/${PYTHON3_BIN_RELDIR_UNIX}:$UNIX_BASE/${PYTHON3_BIN_RELDIR_UNIX}/Scripts"
|
export PATH="$PATH:$UNIX_BASE/${PYTHON3_BIN_RELDIR_UNIX}:$UNIX_BASE/${PYTHON3_BIN_RELDIR_UNIX}/Scripts"
|
||||||
export PYTHON_DIRECT=1
|
export PYTHON_DIRECT=1
|
||||||
export PYTHONUNBUFFERED=1
|
export PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WIN_GIT_PARENT=`dirname "${GIT_BIN_ABSDIR}"`
|
||||||
|
UNIX_GIT_PARENT=`cygpath "$WIN_GIT_PARENT"`
|
||||||
|
BASE_GIT=`basename "${GIT_BIN_ABSDIR}"`
|
||||||
|
UNIX_GIT="$UNIX_GIT_PARENT/$BASE_GIT"
|
||||||
if [[ $# > 0 ]]; then
|
if [[ $# > 0 ]]; then
|
||||||
$UNIX_BASE/${GIT_BIN_RELDIR_UNIX}/bin/bash.exe "$@"
|
"$UNIX_GIT/bin/bash.exe" "$@"
|
||||||
else
|
else
|
||||||
$UNIX_BASE/${GIT_BIN_RELDIR_UNIX}/git-bash.exe &
|
"$UNIX_GIT/git-bash.exe" &
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
if not defined EDITOR set EDITOR=notepad
|
if not defined EDITOR set EDITOR=notepad
|
||||||
set PATH=%~dp0${GIT_BIN_RELDIR}\cmd;%~dp0;%PATH%
|
:: Exclude the current directory when searching for executables.
|
||||||
"%~dp0${GIT_BIN_RELDIR}\${GIT_PROGRAM}" %*
|
:: This is required for the SSO helper to run, which is written in Go.
|
||||||
|
:: Without this set, the SSO helper may throw an error when resolving
|
||||||
|
:: the `git` command (see https://pkg.go.dev/os/exec for more details).
|
||||||
|
set "NoDefaultCurrentDirectoryInExePath=1"
|
||||||
|
set "PATH=${GIT_BIN_ABSDIR}\cmd;%~dp0;%PATH%"
|
||||||
|
"${GIT_BIN_ABSDIR}\${GIT_PROGRAM}" %*
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ IS_WIN = sys.platform == 'win32'
|
|||||||
TEST_MODE = False
|
TEST_MODE = False
|
||||||
|
|
||||||
|
|
||||||
def win_find_git():
|
def win_find_git() -> str:
|
||||||
for elem in os.environ.get('PATH', '').split(os.pathsep):
|
for elem in os.environ.get('PATH', '').split(os.pathsep):
|
||||||
for candidate in ('git.exe', 'git.bat'):
|
for candidate in ('git.exe', 'git.bat'):
|
||||||
path = os.path.join(elem, candidate)
|
path = os.path.join(elem, candidate)
|
||||||
@@ -70,16 +70,37 @@ def win_find_git():
|
|||||||
# so we want to avoid it whenever possible, by extracting the
|
# so we want to avoid it whenever possible, by extracting the
|
||||||
# path to git.exe from git.bat in depot_tools.
|
# path to git.exe from git.bat in depot_tools.
|
||||||
if candidate == 'git.bat':
|
if candidate == 'git.bat':
|
||||||
git_bat = open(path).readlines()
|
path = _extract_git_path_from_git_bat(path)
|
||||||
new_path = os.path.join(elem, git_bat[-1][6:-5])
|
|
||||||
if (git_bat[-1].startswith('"%~dp0')
|
|
||||||
and git_bat[-1].endswith('" %*\n')
|
|
||||||
and new_path.endswith('.exe')):
|
|
||||||
path = new_path
|
|
||||||
return path
|
return path
|
||||||
raise ValueError('Could not find Git on PATH.')
|
raise ValueError('Could not find Git on PATH.')
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_git_path_from_git_bat(path: str) -> str:
|
||||||
|
"""Attempts to extract the path to git.exe from git.bat.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: the absolute path to git.bat.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The absolute path to git.exe if extraction succeeded,
|
||||||
|
otherwise returns the input path to git.bat.
|
||||||
|
"""
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
git_bat = f.readlines()
|
||||||
|
if git_bat[-1].endswith('" %*\n'):
|
||||||
|
if git_bat[-1].startswith('"%~dp0'):
|
||||||
|
# Handle relative path.
|
||||||
|
new_path = os.path.join(os.path.dirname(path),
|
||||||
|
git_bat[-1][6:-5])
|
||||||
|
elif git_bat[-1].startswith('"'):
|
||||||
|
# Handle absolute path.
|
||||||
|
new_path = git_bat[-1][1:-5]
|
||||||
|
|
||||||
|
if new_path.endswith('.exe'):
|
||||||
|
return new_path
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
GIT_EXE = 'git' if not IS_WIN else win_find_git()
|
GIT_EXE = 'git' if not IS_WIN else win_find_git()
|
||||||
|
|
||||||
# The recommended minimum version of Git, as (<major>, <minor>, <patch>).
|
# The recommended minimum version of Git, as (<major>, <minor>, <patch>).
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
:: This is a test git.bat with an absolute path to git.exe.
|
||||||
|
"C:\Absolute\Path\To\Git\cmd\git.exe" %*
|
||||||
4
tests/git_common_test.inputs/testGitBatNonExe/git.bat
Normal file
4
tests/git_common_test.inputs/testGitBatNonExe/git.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
:: This is a test git.bat with an absolute path to git.cmd.
|
||||||
|
"C:\Absolute\Path\To\Git\cmd\git.cmd" %*
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
:: This is a test git.bat with a relative path to git.exe.
|
||||||
|
"%~dp0Relative\Path\To\Git\cmd\git.exe" %*
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
:: This is a test git.bat which does not forward all command line args.
|
||||||
|
"C:\Absolute\Path\To\Git\cmd\git.exe"
|
||||||
@@ -1319,6 +1319,42 @@ class RunWithStderr(GitCommonTestBase):
|
|||||||
self.assertEqual(run_mock.call_count, 1) # 1 + 0 (retry)
|
self.assertEqual(run_mock.call_count, 1) # 1 + 0 (retry)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractGitPathFromGitBatTest(GitCommonTestBase):
|
||||||
|
|
||||||
|
def test_unexpected_format(self):
|
||||||
|
git_bat = os.path.join(DEPOT_TOOLS_ROOT, 'tests',
|
||||||
|
'git_common_test.inputs',
|
||||||
|
'testGitBatUnexpectedFormat', 'git.bat')
|
||||||
|
actual = self.gc._extract_git_path_from_git_bat(git_bat)
|
||||||
|
self.assertEqual(actual, git_bat)
|
||||||
|
|
||||||
|
def test_non_exe(self):
|
||||||
|
git_bat = os.path.join(DEPOT_TOOLS_ROOT, 'tests',
|
||||||
|
'git_common_test.inputs', 'testGitBatNonExe',
|
||||||
|
'git.bat')
|
||||||
|
actual = self.gc._extract_git_path_from_git_bat(git_bat)
|
||||||
|
self.assertEqual(actual, git_bat)
|
||||||
|
|
||||||
|
def test_absolute_path(self):
|
||||||
|
git_bat = os.path.join(DEPOT_TOOLS_ROOT, 'tests',
|
||||||
|
'git_common_test.inputs',
|
||||||
|
'testGitBatAbsolutePath', 'git.bat')
|
||||||
|
actual = self.gc._extract_git_path_from_git_bat(git_bat)
|
||||||
|
expected = 'C:\\Absolute\\Path\\To\\Git\\cmd\\git.exe'
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_relative_path(self):
|
||||||
|
git_bat = os.path.join(DEPOT_TOOLS_ROOT, 'tests',
|
||||||
|
'git_common_test.inputs',
|
||||||
|
'testGitBatRelativePath', 'git.bat')
|
||||||
|
actual = self.gc._extract_git_path_from_git_bat(git_bat)
|
||||||
|
expected = os.path.join(DEPOT_TOOLS_ROOT, 'tests',
|
||||||
|
'git_common_test.inputs',
|
||||||
|
'testGitBatRelativePath',
|
||||||
|
'Relative\\Path\\To\\Git\\cmd\\git.exe')
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(
|
sys.exit(
|
||||||
coverage_utils.covered_main(
|
coverage_utils.covered_main(
|
||||||
|
|||||||
Reference in New Issue
Block a user