xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/private/common_utils.bzl (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1"""Common utilities useful for unifying the behavior of different parts of `cargo-bazel`."""
2
3# buildifier: disable=bzl-visibility
4load("//cargo/private:cargo_utils.bzl", _rust_get_rust_tools = "get_rust_tools")
5load("//rust/platform:triple.bzl", _get_host_triple = "get_host_triple")
6
7get_host_triple = _get_host_triple
8
9CARGO_BAZEL_ISOLATED = "CARGO_BAZEL_ISOLATED"
10CARGO_BAZEL_REPIN = "CARGO_BAZEL_REPIN"
11CARGO_BAZEL_DEBUG = "CARGO_BAZEL_DEBUG"
12REPIN = "REPIN"
13
14CARGO_BAZEL_REPIN_ONLY = "CARGO_BAZEL_REPIN_ONLY"
15
16REPIN_ENV_VARS = [
17    CARGO_BAZEL_REPIN,
18    REPIN,
19]
20
21REPIN_ALLOWLIST_ENV_VAR = CARGO_BAZEL_REPIN_ONLY
22
23_EXECUTE_ERROR_MESSAGE = """\
24Command {args} failed with exit code {exit_code}.
25STDOUT ------------------------------------------------------------------------
26{stdout}
27STDERR ------------------------------------------------------------------------
28{stderr}
29"""
30
31def execute(repository_ctx, args, env = {}, allow_fail = False):
32    """A heler macro for executing some arguments and displaying nicely formatted errors
33
34    Args:
35        repository_ctx (repository_ctx): The rule's context object.
36        args (list): A list of strings which act as `argv` for execution.
37        env (dict, optional): Environment variables to set in the execution environment.
38        allow_fail (bool, optional): Allow the process to fail.
39
40    Returns:
41        struct: The results of `repository_ctx.execute`
42    """
43
44    quiet = repository_ctx.attr.quiet
45    if repository_ctx.os.environ.get(CARGO_BAZEL_DEBUG, None):
46        quiet = False
47
48    result = repository_ctx.execute(
49        args,
50        environment = env,
51        quiet = quiet,
52    )
53
54    if result.return_code and not allow_fail:
55        fail(_EXECUTE_ERROR_MESSAGE.format(
56            args = args,
57            exit_code = result.return_code,
58            stdout = result.stdout,
59            stderr = result.stderr,
60        ))
61
62    return result
63
64def get_rust_tools(repository_ctx, host_triple):
65    """Retrieve a cargo and rustc binary based on the host triple.
66
67    Args:
68        repository_ctx (repository_ctx): The rule's context object.
69        host_triple (struct): A `@rules_rust//rust:triple.bzl%triple` object.
70
71    Returns:
72        struct: A struct containing the expected rust tools
73    """
74
75    # This is a bit hidden but to ensure Cargo behaves consistently based
76    # on the user provided config file, the config file is installed into
77    # the `CARGO_HOME` path. This is done so here since fetching tools
78    # is expected to always occur before any subcommands are run.
79    if repository_ctx.attr.isolated and repository_ctx.attr.cargo_config:
80        cargo_home = _cargo_home_path(repository_ctx)
81        cargo_home_config = repository_ctx.path("{}/config.toml".format(cargo_home))
82        cargo_config = repository_ctx.path(repository_ctx.attr.cargo_config)
83        repository_ctx.symlink(cargo_config, cargo_home_config)
84
85    if repository_ctx.attr.rust_version.startswith(("beta", "nightly")):
86        channel, _, version = repository_ctx.attr.rust_version.partition("/")
87    else:
88        channel = "stable"
89        version = repository_ctx.attr.rust_version
90
91    return _rust_get_rust_tools(
92        cargo_template = repository_ctx.attr.rust_toolchain_cargo_template,
93        rustc_template = repository_ctx.attr.rust_toolchain_rustc_template,
94        host_triple = host_triple,
95        channel = channel,
96        version = version,
97    )
98
99def _cargo_home_path(repository_ctx):
100    """Define a path within the repository to use in place of `CARGO_HOME`
101
102    Args:
103        repository_ctx (repository_ctx): The rules context object
104
105    Returns:
106        path: The path to a directory to use as `CARGO_HOME`
107    """
108    return repository_ctx.path(".cargo_home")
109
110def cargo_environ(repository_ctx):
111    """Define Cargo environment varables for use with `cargo-bazel`
112
113    Args:
114        repository_ctx (repository_ctx): The rules context object
115
116    Returns:
117        dict: A set of environment variables for `cargo-bazel` executions
118    """
119    env = dict()
120
121    if CARGO_BAZEL_ISOLATED in repository_ctx.os.environ:
122        if repository_ctx.os.environ[CARGO_BAZEL_ISOLATED].lower() in ["true", "1", "yes", "on"]:
123            env.update({
124                "CARGO_HOME": str(_cargo_home_path(repository_ctx)),
125            })
126    elif repository_ctx.attr.isolated:
127        env.update({
128            "CARGO_HOME": str(_cargo_home_path(repository_ctx)),
129        })
130
131    return env
132
133def parse_alias_rule(value):
134    """Attempts to parse an `AliasRule` from supplied string.
135
136    Args:
137        value (str): String value to be parsed.
138
139    Returns:
140        value: A Rust compatible `AliasRule`.
141    """
142    if value == None:
143        return None
144
145    if value == "alias" or value == "dbg" or value == "fastbuild" or value == "opt":
146        return value
147
148    if value.count(":") != 2:
149        fail("Invalid custom value for `alias_rule`.\n{}\nValues must be in the format '<label to .bzl>:<rule>'.".format(value))
150
151    split = value.rsplit(":", 1)
152    bzl = Label(split[0])
153    rule = split[1]
154
155    if rule == "alias":
156        fail("Custom value rule cannot be named `alias`.\n{}".format(value))
157
158    return struct(
159        bzl = str(bzl),
160        rule = rule,
161    )
162