Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3199)

Unified Diff: bootstrap/run_helper.py

Issue 1200843003: Added virtualenv for depot_tools (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix on Windows Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: bootstrap/run_helper.py
diff --git a/bootstrap/run_helper.py b/bootstrap/run_helper.py
new file mode 100755
index 0000000000000000000000000000000000000000..5af50f975f52f2680572ef80928f5864954a23d4
--- /dev/null
+++ b/bootstrap/run_helper.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""Code supporting run.py implementation.
+
+Reused across infra/run.py and infra_internal/run.py.
+"""
+
+import os
+import sys
+
+
+def is_in_venv(env_path):
+ """True if already running in virtual env."""
+ abs_prefix = os.path.abspath(sys.prefix)
+ abs_env_path = os.path.abspath(env_path)
+ if abs_prefix == abs_env_path:
+ return True
+ # Ordinarily os.path.abspath(sys.prefix) == env_path is enough. But it doesn't
+ # work when virtual env is deployed as CIPD package. CIPD uses symlinks to
+ # stage files into installation root. When booting venv, something (python
+ # binary itself?) resolves the symlink ENV/bin/python to the target, making
+ # sys.prefix look like "<root>/.cipd/.../ENV". Note that "<root>/ENV" is not
+ # a symlink itself, but "<root>/ENV/bin/python" is.
+ if sys.platform == 'win32':
+ # TODO(vadimsh): Make it work for Win32 too.
+ return False
+ try:
+ return os.path.samefile(
+ os.path.join(abs_prefix, 'bin', 'python'),
+ os.path.join(abs_env_path, 'bin', 'python'))
+ except OSError:
+ return False
+
+
+def boot_venv(script, env_path):
+ """Reexecs the top-level script in a virtualenv (if necessary)."""
+ RUN_PY_RECURSION_BLOCKER = 'RUN_PY_RECURSION'
+
+ if not is_in_venv(env_path):
+ if RUN_PY_RECURSION_BLOCKER in os.environ:
+ print >> sys.stderr, 'TOO MUCH RECURSION IN RUN.PY'
+ sys.exit(-1)
+
+ # not in the venv
+ if sys.platform.startswith('win'):
+ python = os.path.join(env_path, 'Scripts', 'python.exe')
+ else:
+ python = os.path.join(env_path, 'bin', 'python')
+ if os.path.exists(python):
+ os.environ[RUN_PY_RECURSION_BLOCKER] = "1"
+ os.environ.pop('PYTHONPATH', None)
+ os.execv(python, [python, script] + sys.argv[1:])
+ print >> sys.stderr, "Exec is busted :("
+ sys.exit(-1) # should never reach
+
+ print 'You must use the virtualenv in ENV for scripts in the infra repo.'
+ print 'Running `gclient runhooks` will create this environment for you.'
+ sys.exit(1)
+
+ # In case some poor script ends up calling run.py, don't explode them.
+ os.environ.pop(RUN_PY_RECURSION_BLOCKER, None)
+
+
+def run_py_main(args, runpy_path, env_path, package):
+ boot_venv(runpy_path, env_path)
+
+ import argparse
+ import runpy
+ import shlex
+ import textwrap
+
+ import argcomplete
+
+ os.chdir(os.path.dirname(runpy_path))
+
+ # Impersonate the argcomplete 'protocol'
+ completing = os.getenv('_ARGCOMPLETE') == '1'
+ if completing:
+ assert not args
+ line = os.getenv('COMP_LINE')
+ args = shlex.split(line)[1:]
+ if len(args) == 1 and not line.endswith(' '):
+ args = []
+
+ if not args or not args[0].startswith('%s.' % package):
+ commands = []
+ for root, _, files in os.walk(package):
+ if '__main__.py' in files:
+ commands.append(root.replace(os.path.sep, '.'))
+
+ if completing:
+ # Argcomplete is listening for strings on fd 8
+ with os.fdopen(8, 'wb') as f:
+ print >> f, '\n'.join(commands)
+ return
+
+ print textwrap.dedent("""\
+ usage: run.py %s.<module.path.to.tool> [args for tool]
+
+ %s
+
+ Available tools are:""") % (
+ package, sys.modules['__main__'].__doc__.strip())
+ for command in commands:
+ print ' *', command
+ return 1
+
+ if completing:
+ to_nuke = ' ' + args[0]
+ os.environ['COMP_LINE'] = os.environ['COMP_LINE'].replace(to_nuke, '', 1)
+ os.environ['COMP_POINT'] = str(int(os.environ['COMP_POINT']) - len(to_nuke))
+ orig_parse_args = argparse.ArgumentParser.parse_args
+ def new_parse_args(self, *args, **kwargs):
+ argcomplete.autocomplete(self)
+ return orig_parse_args(*args, **kwargs)
+ argparse.ArgumentParser.parse_args = new_parse_args
+ else:
+ # remove the module from sys.argv
+ del sys.argv[1]
+
+ runpy.run_module(args[0], run_name='__main__', alter_sys=True)

Powered by Google App Engine
This is Rietveld 408576698