• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
46def arch_normalized(rctx):
47    """Normalizes the architecture string to match CIPDs naming system.
48
49    Args:
50        rctx: Repository context.
51
52    Returns:
53        str: Normalized architecture.
54    """
55
56    if "arm" in rctx.os.name or "aarch" in rctx.os.arch:
57        return "arm64"
58
59    return "amd64"
60
61def get_client_cipd_version(rctx):
62    """Gets the CIPD client version from the config file.
63
64    Args:
65        rctx: Repository context.
66
67    Returns:
68        str: The CIPD client version tag to use.
69    """
70    return rctx.read(rctx.attr._cipd_version_file).strip()
71
72def _platform(rctx):
73    """Generates a normalized platform string from the host OS/architecture.
74
75    Args:
76        rctx: Repository context.
77    """
78    return "{}-{}".format(platform_normalized(rctx), arch_normalized(rctx))
79
80def get_client_cipd_digest(rctx):
81    """Gets the CIPD client digest from the digest file.
82
83    Args:
84        rctx: Repository context.
85
86    Returns:
87        str: The CIPD client digest.
88    """
89    platform = _platform(rctx)
90    digest_file = rctx.read(rctx.attr._cipd_digest_file)
91    digest_lines = [
92        digest
93        for digest in digest_file.splitlines()
94        # Remove comments from version file
95        if not digest.startswith("#") and digest
96    ]
97
98    for line in digest_lines:
99        (digest_platform, digest_type, digest) = \
100            [element for element in line.split(" ") if element]
101        if digest_platform == platform:
102            if digest_type != "sha256":
103                fail("Bazel only supports sha256 type digests.")
104            return digest
105    fail("Could not find CIPD digest that matches this platform.")
106
107def cipd_client_impl(rctx):
108    """Initializes the CIPD client repository.
109
110    Args:
111        rctx: Repository context.
112    """
113    platform = _platform(rctx)
114    path = "/client?platform={}&version={}".format(
115        platform,
116        get_client_cipd_version(rctx),
117    )
118    rctx.download(
119        output = "cipd",
120        url = _CIPD_HOST + path,
121        sha256 = get_client_cipd_digest(rctx),
122        executable = True,
123    )
124    rctx.file("BUILD", "exports_files([\"cipd\"])")
125
126def cipd_repository_base(rctx):
127    """Populates the base contents of a CIPD repository.
128
129    Args:
130        rctx: Repository context.
131    """
132    cipd_path = rctx.path(rctx.attr._cipd_client)
133    ensure_path = rctx.name + ".ensure"
134    rctx.template(
135        ensure_path,
136        Label("@pigweed//pw_env_setup/bazel/cipd_setup:ensure.tpl"),
137        {
138            "%{path}": rctx.attr.path,
139            "%{tag}": rctx.attr.tag,
140        },
141    )
142    result = rctx.execute([cipd_path, "ensure", "-root", ".", "-ensure-file", ensure_path])
143
144    if result.return_code != 0:
145        fail("Failed to fetch CIPD repository `{}`:\n{}".format(rctx.name, result.stderr))
146
147def cipd_repository_impl(rctx):
148    """Generates an external repository from a CIPD package.
149
150    Args:
151        rctx: Repository context.
152    """
153    cipd_repository_base(rctx)
154
155    # Allow the BUILD file to be overriden in the generated repository.
156    # If unspecified, default to a BUILD file that exposes all of its files.
157    if rctx.attr.build_file:
158        rctx.file("BUILD", rctx.read(rctx.attr.build_file))
159    elif not rctx.path("BUILD").exists and not rctx.path("BUILD.bazel").exists:
160        rctx.file("BUILD", """
161exports_files(glob(["**"]))
162
163filegroup(
164    name = "all",
165    srcs = glob(["**"]),
166    visibility = ["//visibility:public"],
167)
168  """)
169
170def _cipd_path_to_repository_name(path, platform):
171    """ Converts a cipd path to a repository name
172
173    Args:
174        path: The cipd path.
175        platform: The cipd platform name.
176
177    Example:
178        print(_cipd_path_to_repository_name(
179            "infra/3pp/tools/cpython3/windows-amd64",
180            "linux-amd64"
181        ))
182        >> cipd_infra_3pp_tools_cpython3_windows_amd64
183    """
184    return "cipd_" + \
185           path.replace("/", "_") \
186               .replace("${platform}", platform) \
187               .replace("-", "_")
188
189def _cipd_dep_to_cipd_repositories_str(dep, indent):
190    """ Converts a CIPD dependency to a CIPD repositories string
191
192    Args:
193        dep: The CIPD dependency.
194        indent: The indentation to use.
195    """
196    return "\n".join([CIPD_REPOSITORY_TEMPLATE.format(
197        name = _cipd_path_to_repository_name(dep["path"], platform),
198        path = dep["path"].replace("${platform}", platform),
199        tag = dep["tags"][0],
200        indent = indent,
201    ) for platform in dep["platforms"]])
202
203def cipd_deps_impl(repository_ctx):
204    """ Generates a CIPD dependencies file """
205    pigweed_deps = json.decode(
206        repository_ctx.read(repository_ctx.attr._pigweed_packages_json),
207    )["packages"]
208    repository_ctx.file("BUILD", "exports_files(glob([\"**/*\"]))\n")
209
210    repository_ctx.file("cipd_init.bzl", CIPD_INIT_BZL_TEMPLATE.format(
211        cipd_deps = "\n".join([
212            _cipd_dep_to_cipd_repositories_str(dep, indent = "    ")
213            for dep in pigweed_deps
214        ]),
215    ))
216