1# Copyright 2021 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://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, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Internal Bazel helpers for downloading CIPD packages.""" 15 16load( 17 ":cipd_repository_list_templates.bzl", 18 "CIPD_INIT_BZL_TEMPLATE", 19 "CIPD_REPOSITORY_TEMPLATE", 20) 21 22_CIPD_HOST = "https://chrome-infra-packages.appspot.com" 23 24def platform_normalized(rctx): 25 """Normalizes the platform to match CIPDs naming system. 26 27 Args: 28 rctx: Repository context. 29 30 Returns: 31 str: Normalized string. 32 """ 33 34 # Chained if else used because Bazel's rctx.os.name is not stable 35 # between different versions of windows i.e. windows 10 vs windows 36 # server. 37 if "windows" in rctx.os.name: 38 return "windows" 39 elif "linux" == rctx.os.name: 40 return "linux" 41 elif "mac os x" == rctx.os.name: 42 return "mac" 43 else: 44 fail("Could not normalize os:", rctx.os.name) 45 46# TODO(b/234879770): Enable unused variable check. 47# buildifier: disable=unused-variable 48def arch_normalized(rctx): 49 """Normalizes the architecture string to match CIPDs naming system. 50 51 Args: 52 rctx: Repository context. 53 54 Returns: 55 str: Normalized architecture. 56 """ 57 58 # TODO(b/234879770): Find a way to get host architecture information from a 59 # repository context. 60 return "amd64" 61 62def get_client_cipd_version(rctx): 63 """Gets the CIPD client version from the config file. 64 65 Args: 66 rctx: Repository context. 67 68 Returns: 69 str: The CIPD client version tag to use. 70 """ 71 return rctx.read(rctx.attr._cipd_version_file).strip() 72 73def _platform(rctx): 74 return "{}-{}".format(platform_normalized(rctx), arch_normalized(rctx)) 75 76def get_client_cipd_digest(rctx): 77 """Gets the CIPD client digest from the digest file. 78 79 Args: 80 rctx: Repository context. 81 82 Returns: 83 str: The CIPD client digest. 84 """ 85 platform = _platform(rctx) 86 digest_file = rctx.read(rctx.attr._cipd_digest_file) 87 digest_lines = [ 88 digest 89 for digest in digest_file.splitlines() 90 # Remove comments from version file 91 if not digest.startswith("#") and digest 92 ] 93 94 for line in digest_lines: 95 (digest_platform, digest_type, digest) = \ 96 [element for element in line.split(" ") if element] 97 if digest_platform == platform: 98 if digest_type != "sha256": 99 fail("Bazel only supports sha256 type digests.") 100 return digest 101 fail("Could not find CIPD digest that matches this platform.") 102 103def cipd_client_impl(rctx): 104 platform = _platform(rctx) 105 path = "/client?platform={}&version={}".format( 106 platform, 107 get_client_cipd_version(rctx), 108 ) 109 rctx.download( 110 output = "cipd", 111 url = _CIPD_HOST + path, 112 sha256 = get_client_cipd_digest(rctx), 113 executable = True, 114 ) 115 rctx.file("BUILD", "exports_files([\"cipd\"])") 116 117def cipd_repository_base(rctx): 118 cipd_path = rctx.path(rctx.attr._cipd_client).basename 119 ensure_path = rctx.name + ".ensure" 120 rctx.template( 121 ensure_path, 122 Label("@pigweed//pw_env_setup/bazel/cipd_setup:ensure.tpl"), 123 { 124 "%{path}": rctx.attr.path, 125 "%{tag}": rctx.attr.tag, 126 }, 127 ) 128 rctx.execute([cipd_path, "ensure", "-root", ".", "-ensure-file", ensure_path]) 129 130def cipd_repository_impl(rctx): 131 cipd_repository_base(rctx) 132 rctx.file("BUILD", """ 133exports_files(glob([\"**/*\"])) 134 135filegroup( 136 name = "all", 137 srcs = glob(["**/*"]), 138 visibility = ["//visibility:public"], 139) 140""") 141 142def _cipd_path_to_repository_name(path, platform): 143 """ Converts a cipd path to a repository name 144 145 Args: 146 path: The cipd path. 147 platform: The cipd platform name. 148 149 Example: 150 print(_cipd_path_to_repository_name( 151 "infra/3pp/tools/cpython3/windows-amd64", 152 "linux-amd64" 153 )) 154 >> cipd_infra_3pp_tools_cpython3_windows_amd64 155 """ 156 return "cipd_" + \ 157 path.replace("/", "_") \ 158 .replace("${platform}", platform) \ 159 .replace("-", "_") 160 161def _cipd_dep_to_cipd_repositories_str(dep, indent): 162 """ Converts a CIPD dependency to a CIPD repositories string 163 164 Args: 165 dep: The CIPD dependency. 166 indent: The indentation to use. 167 """ 168 return "\n".join([CIPD_REPOSITORY_TEMPLATE.format( 169 name = _cipd_path_to_repository_name(dep["path"], platform), 170 path = dep["path"].replace("${platform}", platform), 171 tag = dep["tags"][0], 172 indent = indent, 173 ) for platform in dep["platforms"]]) 174 175def cipd_deps_impl(repository_ctx): 176 """ Generates a CIPD dependencies file """ 177 pigweed_deps = json.decode( 178 repository_ctx.read(repository_ctx.attr._pigweed_packages_json), 179 )["packages"] + json.decode( 180 repository_ctx.read(repository_ctx.attr._python_packages_json), 181 )["packages"] 182 repository_ctx.file("BUILD", "exports_files(glob([\"**/*\"]))\n") 183 184 repository_ctx.file("cipd_init.bzl", CIPD_INIT_BZL_TEMPLATE.format( 185 cipd_deps = "\n".join([ 186 _cipd_dep_to_cipd_repositories_str(dep, indent = " ") 187 for dep in pigweed_deps 188 ]), 189 )) 190