Move gclient path access to separate module.

gclient_utils.py is a kitchen sink and is for that reason expensive
to import. Move the comparatively cheap and simple path routines
to a new gclient_paths module and use that in gn.py, clang_format.py,
dart_format.py.

(To be able to move FindGclientRoot() to gclient_paths.py,
make it use io.open() instead of FileRead(). FileRead() tries
to paper over invalid utf-8, but that was added for presubmits,
not for .gclient files, so this is hopefully fine.)

Cuts gn.py overhead in half (on my Windows laptop from 0.6s to 0.25s,
still high; on my Mac laptop from 0.1s to 0.05s), and probably helps
the other two too.

Completely remove PathDifference() since it's unused.

Bug: 939959
Change-Id: I6a70f6e4c16062b622fb2df8778e8a598d4cc956
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1512058
Commit-Queue: Nico Weber <thakis@chromium.org>
Reviewed-by: Andrii Shyshkalov <tandrii@chromium.org>
This commit is contained in:
Nico Weber
2019-03-11 16:54:07 +00:00
committed by Commit Bot
parent 3485a263f4
commit 09e0b38f0f
6 changed files with 148 additions and 148 deletions

135
gclient_paths.py Normal file
View File

@@ -0,0 +1,135 @@
# Copyright 2019 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.
# This file is imported by various thin wrappers (around gn, clang-format, ...),
# so it's meant to import very quickly. To keep it that way don't add more
# code, and even more importantly don't add more toplevel import statements.
import os
def FindGclientRoot(from_dir, filename='.gclient'):
"""Tries to find the gclient root."""
real_from_dir = os.path.realpath(from_dir)
path = real_from_dir
while not os.path.exists(os.path.join(path, filename)):
split_path = os.path.split(path)
if not split_path[1]:
return None
path = split_path[0]
# If we did not find the file in the current directory, make sure we are in a
# sub directory that is controlled by this configuration.
if path != real_from_dir:
entries_filename = os.path.join(path, filename + '_entries')
if not os.path.exists(entries_filename):
import sys
# If .gclient_entries does not exist, a previous call to gclient sync
# might have failed. In that case, we cannot verify that the .gclient
# is the one we want to use. In order to not to cause too much trouble,
# just issue a warning and return the path anyway.
print >> sys.stderr, ("%s missing, %s file in parent directory %s might "
"not be the file you want to use." %
(entries_filename, filename, path))
return path
scope = {}
try:
import io
with io.open(entries_filename, encoding='utf-8') as f:
exec(f.read(), scope)
except SyntaxError, e:
SyntaxErrorToError(filename, e)
all_directories = scope['entries'].keys()
path_to_check = real_from_dir[len(path)+1:]
while path_to_check:
if path_to_check in all_directories:
return path
path_to_check = os.path.dirname(path_to_check)
return None
import logging
logging.info('Found gclient root at ' + path)
return path
def GetPrimarySolutionPath():
"""Returns the full path to the primary solution. (gclient_root + src)"""
gclient_root = FindGclientRoot(os.getcwd())
if not gclient_root:
# Some projects might not use .gclient. Try to see whether we're in a git
# checkout.
top_dir = [os.getcwd()]
def filter_fn(line):
repo_root_path = os.path.normpath(line.rstrip('\n'))
if os.path.exists(repo_root_path):
top_dir[0] = repo_root_path
try:
CheckCallAndFilter(["git", "rev-parse", "--show-toplevel"],
print_stdout=False, filter_fn=filter_fn)
except Exception:
pass
top_dir = top_dir[0]
if os.path.exists(os.path.join(top_dir, 'buildtools')):
return top_dir
return None
# Some projects' top directory is not named 'src'.
source_dir_name = GetGClientPrimarySolutionName(gclient_root) or 'src'
return os.path.join(gclient_root, source_dir_name)
def GetBuildtoolsPath():
"""Returns the full path to the buildtools directory.
This is based on the root of the checkout containing the current directory."""
# Overriding the build tools path by environment is highly unsupported and may
# break without warning. Do not rely on this for anything important.
override = os.environ.get('CHROMIUM_BUILDTOOLS_PATH')
if override is not None:
return override
primary_solution = GetPrimarySolutionPath()
if not primary_solution:
return None
buildtools_path = os.path.join(primary_solution, 'buildtools')
if not os.path.exists(buildtools_path):
# Buildtools may be in the gclient root.
gclient_root = FindGclientRoot(os.getcwd())
buildtools_path = os.path.join(gclient_root, 'buildtools')
return buildtools_path
def GetBuildtoolsPlatformBinaryPath():
"""Returns the full path to the binary directory for the current platform."""
buildtools_path = GetBuildtoolsPath()
if not buildtools_path:
return None
if sys.platform.startswith(('cygwin', 'win')):
subdir = 'win'
elif sys.platform == 'darwin':
subdir = 'mac'
elif sys.platform.startswith('linux'):
subdir = 'linux64'
else:
raise Error('Unknown platform: ' + sys.platform)
return os.path.join(buildtools_path, subdir)
def GetExeSuffix():
"""Returns '' or '.exe' depending on how executables work on this platform."""
if sys.platform.startswith(('cygwin', 'win')):
return '.exe'
return ''
def GetGClientPrimarySolutionName(gclient_root_dir_path):
"""Returns the name of the primary solution in the .gclient file specified."""
gclient_config_file = os.path.join(gclient_root_dir_path, '.gclient')
env = {}
execfile(gclient_config_file, env)
solutions = env.get('solutions', [])
if solutions:
return solutions[0].get('name')
return None