1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""The android specific platform implementation module."""
6
7import os
8import subprocess
9
10import cr
11
12# This is the set of environment variables that are not automatically
13# copied back from the envsetup shell
14_IGNORE_ENV = [
15    'SHLVL',  # Because it's nothing to do with envsetup
16    'GYP_GENERATOR_FLAGS',  # because we set them in they gyp handler
17    'GYP_GENERATORS',  # because we set them in they gyp handler
18    'PATH',  # Because it gets a special merge handler
19    'GYP_DEFINES',  # Because it gets a special merge handler
20]
21
22
23class AndroidPlatform(cr.Platform):
24  """The implementation of Platform for the android target."""
25
26  ACTIVE = cr.Config.From(
27      CR_ENVSETUP=os.path.join('{CR_SRC}', 'build', 'android', 'envsetup.sh'),
28      CR_ADB=os.path.join('{CR_SRC}', 'third_party', 'android_tools', 'sdk',
29          'platform-tools', 'adb'),
30      CR_TARGET_SUFFIX='_apk',
31      CR_BINARY=os.path.join('{CR_BUILD_DIR}', 'apks', '{CR_TARGET_NAME}.apk'),
32      CR_ACTION='android.intent.action.VIEW',
33      CR_PACKAGE='com.google.android.apps.{CR_TARGET}',
34      CR_PROCESS='{CR_PACKAGE}',
35      CR_ACTIVITY='.Main',
36      CR_INTENT='{CR_PACKAGE}/{CR_ACTIVITY}',
37      CR_TEST_RUNNER=os.path.join(
38          '{CR_SRC}', 'build', 'android', 'test_runner.py'),
39      CR_ADB_GDB=os.path.join('{CR_SRC}', 'build', 'android', 'adb_gdb'),
40      CR_DEFAULT_TARGET='chrome_shell',
41      GYP_DEF_OS='android'
42  )
43
44  def __init__(self):
45    super(AndroidPlatform, self).__init__()
46    self._env = cr.Config('android-env', literal=True, export=True)
47    self.detected_config.AddChild(self._env)
48    self._env_ready = False
49    self._env_paths = []
50
51  @property
52  def priority(self):
53    return super(AndroidPlatform, self).priority + 1
54
55  def Prepare(self):
56    """Override Prepare from cr.Platform."""
57    super(AndroidPlatform, self).Prepare()
58    try:
59      # capture the result of env setup if we have not already done so
60      if not self._env_ready:
61        # See what the env would be without env setup
62        before = cr.context.exported
63        # Run env setup and capture/parse its output
64        envsetup = 'source {CR_ENVSETUP}'
65        output = cr.Host.CaptureShell(envsetup + ' > /dev/null && env')
66        env_setup = cr.Config('envsetup', literal=True, export=True)
67        for line in output.split('\n'):
68          (key, op, value) = line.partition('=')
69          if op:
70            key = key.strip()
71            if key not in _IGNORE_ENV:
72              env_setup[key] = env_setup.ParseValue(value.strip())
73            if key == 'PATH':
74              self._env_paths = value.strip().split(os.path.pathsep)
75        items = env_setup.exported.items()
76        if not items:
77          # Because of the way envsetup is run, the exit code does not make it
78          # back to us. Instead, we assume if we got no environment at all, it
79          # must have failed.
80          print 'Envsetup failed!'
81          exit(1)
82        # Find all the things that envsetup changed
83        for key, value in env_setup.exported.items():
84          if str(value) != str(before.get(key, None)):
85            self._env[key] = value
86      self._env_ready = True
87    except subprocess.CalledProcessError, e:
88      exit(e.returncode)
89
90  @property
91  def paths(self):
92    return self._env_paths
93
94
95class AndroidInitHook(cr.InitHook):
96  """Android output directory init hook.
97
98  This makes sure that your client is android capable when you try
99  to make and android output directory.
100  """
101
102  @property
103  def enabled(self):
104    return cr.AndroidPlatform.GetInstance().is_active
105
106  def Run(self, old_version, config):
107    _ = old_version, config  # unused
108    # Check we are an android capable client
109    target_os = cr.context.gclient.get('target_os', [])
110    if 'android' in target_os:
111      return
112    url = cr.context.gclient.get('solutions', [{}])[0].get('url')
113    if (url.startswith('https://chrome-internal.googlesource.com/') and
114        url.endswith('/internal/apps.git')):
115      return
116    print 'This client is not android capable.'
117    print 'It can be made capable by adding android to the target_os list'
118    print 'in the .gclient file, and then syncing again.'
119    if not cr.Host.YesNo('Would you like to upgrade this client?'):
120      print 'Abandoning the creation of and android output directory.'
121      exit(1)
122    target_os.append('android')
123    cr.context.gclient['target_os'] = target_os
124    cr.base.client.WriteGClient()
125    print 'Client updated.'
126    print 'You may need to sync before an output directory can be made.'
127    if cr.Host.YesNo('Would you like to sync this client now?'):
128      cr.SyncCommand.Sync(["--nohooks"])
129
130