1#!/usr/bin/env python3 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 17import ast 18import os 19import re 20import subprocess 21import sys 22 23os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..')) 24 25git_hash_pattern = re.compile('[0-9a-f]{40}') 26 27# Parse git hashes from submodules 28git_submodules = subprocess.check_output( 29 'git submodule', shell=True).decode().strip().split('\n') 30git_submodule_hashes = { 31 re.search(git_hash_pattern, s).group() for s in git_submodules 32} 33 34_BAZEL_SKYLIB_DEP_NAME = 'bazel_skylib' 35_BAZEL_TOOLCHAINS_DEP_NAME = 'bazel_toolchains' 36_BAZEL_COMPDB_DEP_NAME = 'bazel_compdb' 37_TWISTED_TWISTED_DEP_NAME = 'com_github_twisted_twisted' 38_YAML_PYYAML_DEP_NAME = 'com_github_yaml_pyyaml' 39_TWISTED_INCREMENTAL_DEP_NAME = 'com_github_twisted_incremental' 40_ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME = 'com_github_zopefoundation_zope_interface' 41_TWISTED_CONSTANTLY_DEP_NAME = 'com_github_twisted_constantly' 42 43_GRPC_DEP_NAMES = [ 44 'upb', 'boringssl', 'zlib', 'com_google_protobuf', 'com_google_googletest', 45 'rules_cc', 'com_github_google_benchmark', 'com_github_cares_cares', 46 'com_google_absl', 'io_opencensus_cpp', 'envoy_api', _BAZEL_SKYLIB_DEP_NAME, 47 _BAZEL_TOOLCHAINS_DEP_NAME, _BAZEL_COMPDB_DEP_NAME, 48 _TWISTED_TWISTED_DEP_NAME, _YAML_PYYAML_DEP_NAME, 49 _TWISTED_INCREMENTAL_DEP_NAME, _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME, 50 _TWISTED_CONSTANTLY_DEP_NAME, 'io_bazel_rules_go', 51 'build_bazel_rules_apple', 'build_bazel_apple_support', 'libuv', 52 'com_googlesource_code_re2', 'bazel_gazelle', 'opencensus_proto', 53 'com_envoyproxy_protoc_gen_validate', 'com_google_googleapis', 54 'com_google_libprotobuf_mutator' 55] 56 57_GRPC_BAZEL_ONLY_DEPS = [ 58 'upb', # third_party/upb is checked in locally 59 'rules_cc', 60 'com_google_absl', 61 'io_opencensus_cpp', 62 _BAZEL_SKYLIB_DEP_NAME, 63 _BAZEL_TOOLCHAINS_DEP_NAME, 64 _BAZEL_COMPDB_DEP_NAME, 65 _TWISTED_TWISTED_DEP_NAME, 66 _YAML_PYYAML_DEP_NAME, 67 _TWISTED_INCREMENTAL_DEP_NAME, 68 _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME, 69 _TWISTED_CONSTANTLY_DEP_NAME, 70 'io_bazel_rules_go', 71 'build_bazel_rules_apple', 72 'build_bazel_apple_support', 73 'com_googlesource_code_re2', 74 'bazel_gazelle', 75 'opencensus_proto', 76 'com_envoyproxy_protoc_gen_validate', 77 'com_google_googleapis', 78 'com_google_libprotobuf_mutator' 79] 80 81 82class BazelEvalState(object): 83 84 def __init__(self, names_and_urls, overridden_name=None): 85 self.names_and_urls = names_and_urls 86 self.overridden_name = overridden_name 87 88 def http_archive(self, **args): 89 self.archive(**args) 90 91 def new_http_archive(self, **args): 92 self.archive(**args) 93 94 def bind(self, **args): 95 pass 96 97 def existing_rules(self): 98 if self.overridden_name: 99 return [self.overridden_name] 100 return [] 101 102 def archive(self, **args): 103 assert self.names_and_urls.get(args['name']) is None 104 if args['name'] in _GRPC_BAZEL_ONLY_DEPS: 105 self.names_and_urls[args['name']] = 'dont care' 106 return 107 url = args.get('url', None) 108 if not url: 109 # we will only be looking for git commit hashes, so concatenating 110 # the urls is fine. 111 url = ' '.join(args['urls']) 112 self.names_and_urls[args['name']] = url 113 114 def git_repository(self, **args): 115 assert self.names_and_urls.get(args['name']) is None 116 if args['name'] in _GRPC_BAZEL_ONLY_DEPS: 117 self.names_and_urls[args['name']] = 'dont care' 118 return 119 self.names_and_urls[args['name']] = args['remote'] 120 121 def grpc_python_deps(self): 122 pass 123 124 125# Parse git hashes from bazel/grpc_deps.bzl {new_}http_archive rules 126with open(os.path.join('bazel', 'grpc_deps.bzl'), 'r') as f: 127 names_and_urls = {} 128 eval_state = BazelEvalState(names_and_urls) 129 bazel_file = f.read() 130 131# grpc_deps.bzl only defines 'grpc_deps' and 'grpc_test_only_deps', add these 132# lines to call them. 133bazel_file += '\ngrpc_deps()\n' 134bazel_file += '\ngrpc_test_only_deps()\n' 135build_rules = { 136 'native': eval_state, 137 'http_archive': lambda **args: eval_state.http_archive(**args), 138 'load': lambda a, b: None, 139 'git_repository': lambda **args: eval_state.git_repository(**args), 140 'grpc_python_deps': lambda: None, 141} 142exec((bazel_file), build_rules) 143for name in _GRPC_DEP_NAMES: 144 assert name in list(names_and_urls.keys()) 145assert len(_GRPC_DEP_NAMES) == len(list(names_and_urls.keys())) 146 147# There are some "bazel-only" deps that are exceptions to this sanity check, 148# we don't require that there is a corresponding git module for these. 149names_without_bazel_only_deps = list(names_and_urls.keys()) 150for dep_name in _GRPC_BAZEL_ONLY_DEPS: 151 names_without_bazel_only_deps.remove(dep_name) 152archive_urls = [names_and_urls[name] for name in names_without_bazel_only_deps] 153workspace_git_hashes = { 154 re.search(git_hash_pattern, url).group() for url in archive_urls 155} 156if len(workspace_git_hashes) == 0: 157 print("(Likely) parse error, did not find any bazel git dependencies.") 158 sys.exit(1) 159 160# Validate the equivalence of the git submodules and Bazel git dependencies. The 161# condition we impose is that there is a git submodule for every dependency in 162# the workspace, but not necessarily conversely. E.g. Bloaty is a dependency 163# not used by any of the targets built by Bazel. 164if len(workspace_git_hashes - git_submodule_hashes) > 0: 165 print( 166 "Found discrepancies between git submodules and Bazel WORKSPACE dependencies" 167 ) 168 print("workspace_git_hashes: %s" % workspace_git_hashes) 169 print("git_submodule_hashes: %s" % git_submodule_hashes) 170 print("workspace_git_hashes - git_submodule_hashes: %s" % 171 (workspace_git_hashes - git_submodule_hashes)) 172 sys.exit(1) 173 174# Also check that we can override each dependency 175for name in _GRPC_DEP_NAMES: 176 names_and_urls_with_overridden_name = {} 177 state = BazelEvalState(names_and_urls_with_overridden_name, 178 overridden_name=name) 179 rules = { 180 'native': state, 181 'http_archive': lambda **args: state.http_archive(**args), 182 'load': lambda a, b: None, 183 'git_repository': lambda **args: state.git_repository(**args), 184 'grpc_python_deps': lambda *args, **kwargs: None, 185 } 186 exec((bazel_file), rules) 187 assert name not in list(names_and_urls_with_overridden_name.keys()) 188 189sys.exit(0) 190