aboutsummaryrefslogtreecommitdiff
path: root/pw_env_setup/bazel/cipd_setup/internal/cipd_internal.bzl
blob: 6bbc06585082809986f1c42d922e750934d057c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Copyright 2021 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Internal Bazel helpers for downloading CIPD packages."""

load(
    ":cipd_repository_list_templates.bzl",
    "CIPD_INIT_BZL_TEMPLATE",
    "CIPD_REPOSITORY_TEMPLATE",
)

_CIPD_HOST = "https://chrome-infra-packages.appspot.com"

def platform_normalized(rctx):
    """Normalizes the platform to match CIPDs naming system.

    Args:
        rctx: Repository context.

    Returns:
        str: Normalized string.
    """

    # Chained if else used because Bazel's rctx.os.name is not stable
    # between different versions of windows i.e. windows 10 vs windows
    # server.
    if "windows" in rctx.os.name:
        return "windows"
    elif "linux" == rctx.os.name:
        return "linux"
    elif "mac os x" == rctx.os.name:
        return "mac"
    else:
        fail("Could not normalize os:", rctx.os.name)

# TODO(b/234879770): Enable unused variable check.
# buildifier: disable=unused-variable
def arch_normalized(rctx):
    """Normalizes the architecture string to match CIPDs naming system.

    Args:
        rctx: Repository context.

    Returns:
        str: Normalized architecture.
    """

    # TODO(b/234879770): Find a way to get host architecture information from a
    # repository context.
    return "amd64"

def get_client_cipd_version(rctx):
    """Gets the CIPD client version from the config file.

    Args:
        rctx: Repository context.

    Returns:
        str: The CIPD client version tag to use.
    """
    return rctx.read(rctx.attr._cipd_version_file).strip()

def _platform(rctx):
    return "{}-{}".format(platform_normalized(rctx), arch_normalized(rctx))

def get_client_cipd_digest(rctx):
    """Gets the CIPD client digest from the digest file.

    Args:
        rctx: Repository context.

    Returns:
        str: The CIPD client digest.
    """
    platform = _platform(rctx)
    digest_file = rctx.read(rctx.attr._cipd_digest_file)
    digest_lines = [
        digest
        for digest in digest_file.splitlines()
        # Remove comments from version file
        if not digest.startswith("#") and digest
    ]

    for line in digest_lines:
        (digest_platform, digest_type, digest) = \
            [element for element in line.split(" ") if element]
        if digest_platform == platform:
            if digest_type != "sha256":
                fail("Bazel only supports sha256 type digests.")
            return digest
    fail("Could not find CIPD digest that matches this platform.")

def cipd_client_impl(rctx):
    platform = _platform(rctx)
    path = "/client?platform={}&version={}".format(
        platform,
        get_client_cipd_version(rctx),
    )
    rctx.download(
        output = "cipd",
        url = _CIPD_HOST + path,
        sha256 = get_client_cipd_digest(rctx),
        executable = True,
    )
    rctx.file("BUILD", "exports_files([\"cipd\"])")

def cipd_repository_base(rctx):
    cipd_path = rctx.path(rctx.attr._cipd_client).basename
    ensure_path = rctx.name + ".ensure"
    rctx.template(
        ensure_path,
        Label("@pigweed//pw_env_setup/bazel/cipd_setup:ensure.tpl"),
        {
            "%{path}": rctx.attr.path,
            "%{tag}": rctx.attr.tag,
        },
    )
    rctx.execute([cipd_path, "ensure", "-root", ".", "-ensure-file", ensure_path])

def cipd_repository_impl(rctx):
    cipd_repository_base(rctx)
    rctx.file("BUILD", """
exports_files(glob([\"**/*\"]))

filegroup(
    name = "all",
    srcs = glob(["**/*"]),
    visibility = ["//visibility:public"],
)
""")

def _cipd_path_to_repository_name(path, platform):
    """ Converts a cipd path to a repository name

    Args:
        path: The cipd path.
        platform: The cipd platform name.

    Example:
        print(_cipd_path_to_repository_name(
            "infra/3pp/tools/cpython3/windows-amd64",
            "linux-amd64"
        ))
        >> cipd_infra_3pp_tools_cpython3_windows_amd64
    """
    return "cipd_" + \
           path.replace("/", "_") \
               .replace("${platform}", platform) \
               .replace("-", "_")

def _cipd_dep_to_cipd_repositories_str(dep, indent):
    """ Converts a CIPD dependency to a CIPD repositories string

    Args:
        dep: The CIPD dependency.
        indent: The indentation to use.
    """
    return "\n".join([CIPD_REPOSITORY_TEMPLATE.format(
        name = _cipd_path_to_repository_name(dep["path"], platform),
        path = dep["path"].replace("${platform}", platform),
        tag = dep["tags"][0],
        indent = indent,
    ) for platform in dep["platforms"]])

def cipd_deps_impl(repository_ctx):
    """ Generates a CIPD dependencies file """
    pigweed_deps = json.decode(
        repository_ctx.read(repository_ctx.attr._pigweed_packages_json),
    )["packages"] + json.decode(
        repository_ctx.read(repository_ctx.attr._python_packages_json),
    )["packages"]
    repository_ctx.file("BUILD", "exports_files(glob([\"**/*\"]))\n")

    repository_ctx.file("cipd_init.bzl", CIPD_INIT_BZL_TEMPLATE.format(
        cipd_deps = "\n".join([
            _cipd_dep_to_cipd_repositories_str(dep, indent = "    ")
            for dep in pigweed_deps
        ]),
    ))