1#!/usr/bin/env python
2# Copyright (c) 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5#
6"""Logic to generate lists of DEPS used by various parts of
7the android_webview continuous integration (buildbot) infrastructure.
8
9Note: The root Chromium project (which is not explicitly listed here)
10has a couple of third_party libraries checked in directly into it. This means
11that the list of third parties present in this file is not a comprehensive
12list of third party android_webview dependencies.
13"""
14
15import argparse
16import json
17import logging
18import os
19import sys
20
21
22class DepsWhitelist(object):
23  def __init__(self):
24    # If a new DEPS entry is needed for the AOSP bot to compile please add it
25    # here first.
26    # This is a staging area for deps that are accepted by the android_webview
27    # team and are in the process of having the required branches being created
28    # in the Android tree.
29    self._compile_but_not_snapshot_dependencies = [
30      'third_party/mesa/src',
31    ]
32
33    # Dependencies that need to be merged into the Android tree.
34    self._snapshot_into_android_dependencies = [
35      'sdch/open-vcdiff',
36      'testing/gtest',
37      'third_party/WebKit',
38      'third_party/angle_dx11',
39      ('third_party/eyesfree/src/android/java/src/com/googlecode/eyesfree/'
40       'braille'),
41      'third_party/freetype',
42      'third_party/icu',
43      'third_party/leveldatabase/src',
44      'third_party/libjingle/source/talk',
45      'third_party/libphonenumber/src/phonenumbers',
46      'third_party/libphonenumber/src/resources',
47      'third_party/openssl',
48      'third_party/opus/src',
49      'third_party/ots',
50      'third_party/skia/gyp',
51      'third_party/skia/include',
52      'third_party/skia/src',
53      'third_party/smhasher/src',
54      'third_party/v8-i18n',
55      'third_party/yasm/source/patched-yasm',
56      'tools/grit',
57      'tools/gyp',
58      'v8',
59    ]
60
61    # Dependencies required to build android_webview.
62    self._compile_dependencies = (self._snapshot_into_android_dependencies +
63                                  self._compile_but_not_snapshot_dependencies)
64
65    # Dependencies required to run android_webview tests but not required to
66    # compile.
67    self._test_data_dependencies = [
68      'chrome/test/data/perf/third_party/octane',
69    ]
70
71  @staticmethod
72  def _read_deps_file(deps_file_path):
73    class FileImplStub(object):
74      """Stub for the File syntax."""
75      def __init__(self, file_location):
76        pass
77
78      @staticmethod
79      def GetPath():
80        return ''
81
82      @staticmethod
83      def GetFilename():
84        return ''
85
86      @staticmethod
87      def GetRevision():
88        return None
89
90    def from_stub(__, _=None):
91      """Stub for the From syntax."""
92      return ''
93
94    class VarImpl(object):
95      def __init__(self, custom_vars, local_scope):
96        self._custom_vars = custom_vars
97        self._local_scope = local_scope
98
99      def Lookup(self, var_name):
100        """Implements the Var syntax."""
101        if var_name in self._custom_vars:
102          return self._custom_vars[var_name]
103        elif var_name in self._local_scope.get("vars", {}):
104          return self._local_scope["vars"][var_name]
105        raise Exception("Var is not defined: %s" % var_name)
106
107    local_scope = {}
108    var = VarImpl({}, local_scope)
109    global_scope = {
110        'File': FileImplStub,
111        'From': from_stub,
112        'Var': var.Lookup,
113        'deps_os': {},
114    }
115    execfile(deps_file_path, global_scope, local_scope)
116    deps = local_scope.get('deps', {})
117    deps_os = local_scope.get('deps_os', {})
118    for os_specific_deps in deps_os.itervalues():
119      deps.update(os_specific_deps)
120    return deps.keys()
121
122  def _make_gclient_blacklist(self, deps_file_path, whitelisted_deps):
123    """Calculates the list of deps that need to be excluded from the deps_file
124    so that the only deps left are the one in the whitelist."""
125    all_deps = self._read_deps_file(deps_file_path)
126    # The list of deps read from the DEPS file are prefixed with the source
127    # tree root, which is 'src' for Chromium.
128    def prepend_root(path):
129      return os.path.join('src', path)
130    whitelisted_deps = map(prepend_root, whitelisted_deps)
131    deps_blacklist = set(all_deps).difference(set(whitelisted_deps))
132    return dict(map(lambda(x): (x, None), deps_blacklist))
133
134  def get_deps_for_android_build(self, deps_file_path):
135    """This is used to calculate the custom_deps list for the Android bot.
136    """
137    if not deps_file_path:
138      raise Exception('You need to specify a DEPS file path.')
139    return self._make_gclient_blacklist(deps_file_path,
140                                        self._compile_dependencies)
141
142  def get_deps_for_android_build_and_test(self, deps_file_path):
143    """This is used to calculate the custom_deps list for the Android perf bot.
144    """
145    if not deps_file_path:
146      raise Exception('You need to specify a DEPS file path.')
147    return self._make_gclient_blacklist(deps_file_path,
148                                        self._compile_dependencies +
149                                        self._test_data_dependencies)
150
151  def get_deps_for_android_merge(self, _):
152    """Calculates the list of deps that need to be merged into the Android tree
153    in order to build the C++ and Java android_webview code."""
154    return self._snapshot_into_android_dependencies
155
156  def get_deps_for_license_check(self, _):
157    """Calculates the list of deps that need to be checked for Android license
158    compatibility"""
159    return self._compile_dependencies
160
161  def execute_method(self, method_name, deps_file_path):
162    methods = {
163      'android_build': self.get_deps_for_android_build,
164      'android_build_and_test':
165        self.get_deps_for_android_build_and_test,
166      'android_merge': self.get_deps_for_android_merge,
167      'license_check': self.get_deps_for_license_check
168    }
169    if not method_name in methods:
170      raise Exception('Method name %s is not valid. Valid choices are %s' %
171                      (method_name, methods.keys()))
172    return methods[method_name](deps_file_path)
173
174def main():
175  parser = argparse.ArgumentParser()
176  parser.add_argument('--method', help='Method to use to fetch from whitelist.',
177                      required=True)
178  parser.add_argument('--path-to-deps', help='Path to DEPS file.')
179  parser.add_argument('--output-json', help='Name of file to write output to.')
180  parser.add_argument('verbose', action='store_true', default=False)
181  opts = parser.parse_args()
182
183  logging.getLogger().setLevel(logging.DEBUG if opts.verbose else logging.WARN)
184
185  deps_whitelist = DepsWhitelist()
186  blacklist = deps_whitelist.execute_method(opts.method, opts.path_to_deps)
187
188  if (opts.output_json):
189    output_dict = {
190        'blacklist' : blacklist
191    }
192    with open(opts.output_json, 'w') as output_json_file:
193      json.dump(output_dict, output_json_file)
194  else:
195    print blacklist
196
197  return 0
198
199
200if __name__ == '__main__':
201  sys.exit(main())
202