1# Copyright 2019 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Buildgen attribute validation plugin.""" 15 16 17def anything(): 18 return lambda v: None 19 20 21def one_of(values): 22 return lambda v: ( 23 "{0} is not in [{1}]".format(v, values) if v not in values else None 24 ) 25 26 27def subset_of(values): 28 return lambda v: ( 29 "{0} is not subset of [{1}]".format(v, values) 30 if not all(e in values for e in v) 31 else None 32 ) 33 34 35VALID_ATTRIBUTE_KEYS_MAP = { 36 "filegroup": { 37 "deps": anything(), 38 "headers": anything(), 39 "plugin": anything(), 40 "public_headers": anything(), 41 "src": anything(), 42 "uses": anything(), 43 }, 44 "lib": { 45 "asm_src": anything(), 46 "baselib": anything(), 47 "boringssl": one_of((True,)), 48 "build_system": anything(), 49 "build": anything(), 50 "cmake_target": anything(), 51 "defaults": anything(), 52 "deps_linkage": one_of(("static",)), 53 "deps": anything(), 54 "dll": one_of((True, "only")), 55 "filegroups": anything(), 56 "generate_plugin_registry": anything(), 57 "headers": anything(), 58 "language": one_of(("c", "c++", "csharp")), 59 "LDFLAGS": anything(), 60 "platforms": subset_of(("linux", "mac", "posix", "windows")), 61 "public_headers": anything(), 62 "secure": one_of(("check", True, False)), 63 "src": anything(), 64 "vs_proj_dir": anything(), 65 "zlib": one_of((True,)), 66 }, 67 "target": { 68 "args": anything(), 69 "benchmark": anything(), 70 "boringssl": one_of((True,)), 71 "build": anything(), 72 "ci_platforms": anything(), 73 "corpus_dirs": anything(), 74 "cpu_cost": anything(), 75 "defaults": anything(), 76 "deps": anything(), 77 "dict": anything(), 78 "exclude_configs": anything(), 79 "exclude_iomgrs": anything(), 80 "excluded_poll_engines": anything(), 81 "filegroups": anything(), 82 "flaky": one_of((True, False)), 83 "gtest": one_of((True, False)), 84 "headers": anything(), 85 "language": one_of(("c", "c89", "c++", "csharp")), 86 "maxlen": anything(), 87 "platforms": subset_of(("linux", "mac", "posix", "windows")), 88 "plugin_option": anything(), 89 "run": one_of((True, False)), 90 "secure": one_of(("check", True, False)), 91 "src": anything(), 92 "timeout_seconds": anything(), 93 "uses_polling": anything(), 94 "vs_proj_dir": anything(), 95 "zlib": one_of((True,)), 96 }, 97 "external_proto_library": { 98 "destination": anything(), 99 "proto_prefix": anything(), 100 "urls": anything(), 101 "hash": anything(), 102 "strip_prefix": anything(), 103 }, 104} 105 106 107def check_attributes(entity, kind, errors): 108 attributes = VALID_ATTRIBUTE_KEYS_MAP[kind] 109 name = entity.get("name", anything()) 110 for key, value in list(entity.items()): 111 if key == "name": 112 continue 113 validator = attributes.get(key) 114 if validator: 115 error = validator(value) 116 if error: 117 errors.append( 118 "{0}({1}) has an invalid value for '{2}': {3}".format( 119 name, kind, key, error 120 ) 121 ) 122 else: 123 errors.append( 124 "{0}({1}) has an invalid attribute '{2}'".format( 125 name, kind, key 126 ) 127 ) 128 129 130def mako_plugin(dictionary): 131 """The exported plugin code for check_attr. 132 133 This validates that filegroups, libs, and target can have only valid 134 attributes. This is mainly for preventing build.yaml from having 135 unnecessary and misleading attributes accidentally. 136 """ 137 138 errors = [] 139 for filegroup in dictionary.get("filegroups", {}): 140 check_attributes(filegroup, "filegroup", errors) 141 for lib in dictionary.get("libs", {}): 142 check_attributes(lib, "lib", errors) 143 for target in dictionary.get("targets", {}): 144 check_attributes(target, "target", errors) 145 for target in dictionary.get("external_proto_libraries", {}): 146 check_attributes(target, "external_proto_library", errors) 147 if errors: 148 raise Exception("\n".join(errors)) 149