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