• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Copyright 2016 gRPC authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from __future__ import print_function
18
19import ast
20import os
21import re
22import subprocess
23import sys
24
25os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
26
27git_hash_pattern = re.compile('[0-9a-f]{40}')
28
29# Parse git hashes from submodules
30git_submodules = subprocess.check_output('git submodule',
31                                         shell=True).strip().split('\n')
32git_submodule_hashes = {
33    re.search(git_hash_pattern, s).group() for s in git_submodules
34}
35
36_BAZEL_SKYLIB_DEP_NAME = 'bazel_skylib'
37_BAZEL_TOOLCHAINS_DEP_NAME = 'bazel_toolchains'
38_TWISTED_TWISTED_DEP_NAME = 'com_github_twisted_twisted'
39_YAML_PYYAML_DEP_NAME = 'com_github_yaml_pyyaml'
40_TWISTED_INCREMENTAL_DEP_NAME = 'com_github_twisted_incremental'
41_ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME = 'com_github_zopefoundation_zope_interface'
42_TWISTED_CONSTANTLY_DEP_NAME = 'com_github_twisted_constantly'
43
44_GRPC_DEP_NAMES = [
45    'upb',
46    'boringssl',
47    'zlib',
48    'com_google_protobuf',
49    'com_google_googletest',
50    'rules_cc',
51    'com_github_gflags_gflags',
52    'com_github_google_benchmark',
53    'com_github_cares_cares',
54    'com_google_absl',
55    'io_opencensus_cpp',
56    'envoy_api',
57    _BAZEL_SKYLIB_DEP_NAME,
58    _BAZEL_TOOLCHAINS_DEP_NAME,
59    _TWISTED_TWISTED_DEP_NAME,
60    _YAML_PYYAML_DEP_NAME,
61    _TWISTED_INCREMENTAL_DEP_NAME,
62    _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
63    _TWISTED_CONSTANTLY_DEP_NAME,
64    'io_bazel_rules_go',
65    'build_bazel_rules_apple',
66    'build_bazel_apple_support',
67    'libuv',
68    'com_github_google_re2',
69]
70
71_GRPC_BAZEL_ONLY_DEPS = [
72    'upb',  # third_party/upb is checked in locally
73    'rules_cc',
74    'com_google_absl',
75    'io_opencensus_cpp',
76    _BAZEL_SKYLIB_DEP_NAME,
77    _BAZEL_TOOLCHAINS_DEP_NAME,
78    _TWISTED_TWISTED_DEP_NAME,
79    _YAML_PYYAML_DEP_NAME,
80    _TWISTED_INCREMENTAL_DEP_NAME,
81    _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
82    _TWISTED_CONSTANTLY_DEP_NAME,
83    'io_bazel_rules_go',
84    'build_bazel_rules_apple',
85    'build_bazel_apple_support',
86]
87
88
89class BazelEvalState(object):
90
91    def __init__(self, names_and_urls, overridden_name=None):
92        self.names_and_urls = names_and_urls
93        self.overridden_name = overridden_name
94
95    def http_archive(self, **args):
96        self.archive(**args)
97
98    def new_http_archive(self, **args):
99        self.archive(**args)
100
101    def bind(self, **args):
102        pass
103
104    def existing_rules(self):
105        if self.overridden_name:
106            return [self.overridden_name]
107        return []
108
109    def archive(self, **args):
110        assert self.names_and_urls.get(args['name']) is None
111        if args['name'] in _GRPC_BAZEL_ONLY_DEPS:
112            self.names_and_urls[args['name']] = 'dont care'
113            return
114        url = args.get('url', None)
115        if not url:
116            # we will only be looking for git commit hashes, so concatenating
117            # the urls is fine.
118            url = ' '.join(args['urls'])
119        self.names_and_urls[args['name']] = url
120
121    def git_repository(self, **args):
122        assert self.names_and_urls.get(args['name']) is None
123        if args['name'] in _GRPC_BAZEL_ONLY_DEPS:
124            self.names_and_urls[args['name']] = 'dont care'
125            return
126        self.names_and_urls[args['name']] = args['remote']
127
128    def grpc_python_deps(self):
129        pass
130
131
132# Parse git hashes from bazel/grpc_deps.bzl {new_}http_archive rules
133with open(os.path.join('bazel', 'grpc_deps.bzl'), 'r') as f:
134    names_and_urls = {}
135    eval_state = BazelEvalState(names_and_urls)
136    bazel_file = f.read()
137
138# grpc_deps.bzl only defines 'grpc_deps' and 'grpc_test_only_deps', add these
139# lines to call them.
140bazel_file += '\ngrpc_deps()\n'
141bazel_file += '\ngrpc_test_only_deps()\n'
142build_rules = {
143    'native': eval_state,
144    'http_archive': lambda **args: eval_state.http_archive(**args),
145    'load': lambda a, b: None,
146    'git_repository': lambda **args: eval_state.git_repository(**args),
147    'grpc_python_deps': lambda: None,
148}
149exec(bazel_file) in build_rules
150for name in _GRPC_DEP_NAMES:
151    assert name in names_and_urls.keys()
152assert len(_GRPC_DEP_NAMES) == len(names_and_urls.keys())
153
154# There are some "bazel-only" deps that are exceptions to this sanity check,
155# we don't require that there is a corresponding git module for these.
156names_without_bazel_only_deps = names_and_urls.keys()
157for dep_name in _GRPC_BAZEL_ONLY_DEPS:
158    names_without_bazel_only_deps.remove(dep_name)
159archive_urls = [names_and_urls[name] for name in names_without_bazel_only_deps]
160workspace_git_hashes = {
161    re.search(git_hash_pattern, url).group() for url in archive_urls
162}
163if len(workspace_git_hashes) == 0:
164    print("(Likely) parse error, did not find any bazel git dependencies.")
165    sys.exit(1)
166
167# Validate the equivalence of the git submodules and Bazel git dependencies. The
168# condition we impose is that there is a git submodule for every dependency in
169# the workspace, but not necessarily conversely. E.g. Bloaty is a dependency
170# not used by any of the targets built by Bazel.
171if len(workspace_git_hashes - git_submodule_hashes) > 0:
172    print(
173        "Found discrepancies between git submodules and Bazel WORKSPACE dependencies"
174    )
175    print("workspace_git_hashes: %s" % workspace_git_hashes)
176    print("git_submodule_hashes: %s" % git_submodule_hashes)
177    print("workspace_git_hashes - git_submodule_hashes: %s" %
178          (workspace_git_hashes - git_submodule_hashes))
179    sys.exit(1)
180
181# Also check that we can override each dependency
182for name in _GRPC_DEP_NAMES:
183    names_and_urls_with_overridden_name = {}
184    state = BazelEvalState(names_and_urls_with_overridden_name,
185                           overridden_name=name)
186    rules = {
187        'native': state,
188        'http_archive': lambda **args: state.http_archive(**args),
189        'load': lambda a, b: None,
190        'git_repository': lambda **args: state.git_repository(**args),
191        'grpc_python_deps': lambda *args, **kwargs: None,
192    }
193    exec(bazel_file) in rules
194    assert name not in names_and_urls_with_overridden_name.keys()
195
196sys.exit(0)
197