aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt
blob: fdc63cb6b7c3dcf788ae8ae1a3ef6a263ac8a21a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/python

import os
import re
import sys


PYTHON_BINARY = '%python_binary%'

# Find a file in a given search path.
def SearchPath(name):
  search_path = os.getenv('PATH', os.defpath).split(os.pathsep)
  for directory in search_path:
    if directory == '': continue
    path = os.path.join(directory, name)
    if os.path.exists(path):
      return path
  return None

# Find the real Python binary if it's not a normal absolute path
def FindPythonBinary():
  if PYTHON_BINARY.startswith('//'):
    # Case 1: Path is a label. Not supported yet.
    raise AssertionError(
      'Bazel does not support execution of Python interpreters via labels yet')
  elif PYTHON_BINARY.startswith('/'):
    # Case 2: Absolute path.
    return PYTHON_BINARY
  elif '/' in PYTHON_BINARY:
    # Case 3: Path is relative to current working directory.
    return os.path.join(os.getcwd(), PYTHON_BINARY)
  else:
    # Case 4: Path has to be looked up in the search path.
    return SearchPath(PYTHON_BINARY)

def Main():
  args = sys.argv[1:]

  new_env = {}

  # Follow symlinks, looking for my module space
  stub_filename = os.path.abspath(sys.argv[0])
  while True:
    # Found it?
    module_space = stub_filename + '.runfiles'
    if os.path.isdir(module_space):
      break

    # Follow a symlink, try again?
    if os.path.islink(stub_filename):
      link = os.readlink(stub_filename)
      # Absolutize
      stub_filename = os.path.join(os.path.dirname(stub_filename), link)
      continue

    matchobj = re.match("(.*\.runfiles)/.*", os.path.abspath(sys.argv[0]))
    if matchobj:
      module_space = matchobj.group(1)
      break

    raise AssertionError('Cannot find .runfiles directory for %s' %
                         sys.argv[0])

  python_path_entries = [module_space]

  external_dir = os.path.join(module_space, 'external')
  if os.path.isdir(external_dir):
    external_entries = [os.path.join(external_dir, d) for d in os.listdir(external_dir)]
    repositories = [d for d in external_entries if os.path.isdir(d)]
    python_path_entries += repositories

  old_python_path = os.environ.get('PYTHONPATH')
  python_path = ':'.join(python_path_entries)
  if old_python_path:
    python_path += ':' + old_python_path

  new_env['PYTHONPATH'] = python_path

  # Now look for my main python source file.
  # The magic string percent-main-percent is replaced with the filename of the
  # main file of the Python binary in BazelPythonSemantics.java.
  main_filename = os.path.join(module_space, '%main%')
  assert os.path.exists(main_filename), \
         'Cannot exec() %r: file not found.' % main_filename
  assert os.access(main_filename, os.R_OK), \
         'Cannot exec() %r: file not readable.' % main_filename

  program = python_program = FindPythonBinary()
  if python_program is None:
    raise AssertionError('Could not find python binary: ' + PYTHON_BINARY)
  args = [python_program, main_filename] + args

  os.environ.update(new_env)

  try:
    sys.stdout.flush()
    os.execv(program, args)
  except EnvironmentError as e:
    # This exception occurs when os.execv() fails for some reason.
    if not getattr(e, 'filename', None):
      e.filename = program  # Add info to error message
    raise

if __name__ == '__main__':
  Main()