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 = {}): 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 39 Returns: 40 struct: The results of `repository_ctx.execute` 41 """ 42 43 quiet = repository_ctx.attr.quiet 44 if repository_ctx.os.environ.get(CARGO_BAZEL_DEBUG, None): 45 quiet = False 46 47 result = repository_ctx.execute( 48 args, 49 environment = env, 50 quiet = quiet, 51 ) 52 53 if result.return_code: 54 fail(_EXECUTE_ERROR_MESSAGE.format( 55 args = args, 56 exit_code = result.return_code, 57 stdout = result.stdout, 58 stderr = result.stderr, 59 )) 60 61 return result 62 63def get_rust_tools(repository_ctx, host_triple): 64 """Retrieve a cargo and rustc binary based on the host triple. 65 66 Args: 67 repository_ctx (repository_ctx): The rule's context object. 68 host_triple (struct): A `@rules_rust//rust:triple.bzl%triple` object. 69 70 Returns: 71 struct: A struct containing the expected rust tools 72 """ 73 74 # This is a bit hidden but to ensure Cargo behaves consistently based 75 # on the user provided config file, the config file is installed into 76 # the `CARGO_HOME` path. This is done so here since fetching tools 77 # is expected to always occur before any subcommands are run. 78 if repository_ctx.attr.isolated and repository_ctx.attr.cargo_config: 79 cargo_home = _cargo_home_path(repository_ctx) 80 cargo_home_config = repository_ctx.path("{}/config.toml".format(cargo_home)) 81 cargo_config = repository_ctx.path(repository_ctx.attr.cargo_config) 82 repository_ctx.symlink(cargo_config, cargo_home_config) 83 84 if repository_ctx.attr.rust_version.startswith(("beta", "nightly")): 85 channel, _, version = repository_ctx.attr.rust_version.partition("/") 86 else: 87 channel = "stable" 88 version = repository_ctx.attr.rust_version 89 90 return _rust_get_rust_tools( 91 cargo_template = repository_ctx.attr.rust_toolchain_cargo_template, 92 rustc_template = repository_ctx.attr.rust_toolchain_rustc_template, 93 host_triple = host_triple, 94 channel = channel, 95 version = version, 96 ) 97 98def _cargo_home_path(repository_ctx): 99 """Define a path within the repository to use in place of `CARGO_HOME` 100 101 Args: 102 repository_ctx (repository_ctx): The rules context object 103 104 Returns: 105 path: The path to a directory to use as `CARGO_HOME` 106 """ 107 return repository_ctx.path(".cargo_home") 108 109def cargo_environ(repository_ctx): 110 """Define Cargo environment varables for use with `cargo-bazel` 111 112 Args: 113 repository_ctx (repository_ctx): The rules context object 114 115 Returns: 116 dict: A set of environment variables for `cargo-bazel` executions 117 """ 118 env = dict() 119 120 if CARGO_BAZEL_ISOLATED in repository_ctx.os.environ: 121 if repository_ctx.os.environ[CARGO_BAZEL_ISOLATED].lower() in ["true", "1", "yes", "on"]: 122 env.update({ 123 "CARGO_HOME": str(_cargo_home_path(repository_ctx)), 124 }) 125 elif repository_ctx.attr.isolated: 126 env.update({ 127 "CARGO_HOME": str(_cargo_home_path(repository_ctx)), 128 }) 129 130 return env 131 132def parse_alias_rule(value): 133 """Attempts to parse an `AliasRule` from supplied string. 134 135 Args: 136 value (str): String value to be parsed. 137 138 Returns: 139 value: A Rust compatible `AliasRule`. 140 """ 141 if value == None: 142 return None 143 144 if value == "alias" or value == "dbg" or value == "fastbuild" or value == "opt": 145 return value 146 147 if value.count(":") != 2: 148 fail("Invalid custom value for `alias_rule`.\n{}\nValues must be in the format '<label to .bzl>:<rule>'.".format(value)) 149 150 split = value.rsplit(":", 1) 151 bzl = Label(split[0]) 152 rule = split[1] 153 154 if rule == "alias": 155 fail("Custom value rule cannot be named `alias`.\n{}".format(value)) 156 157 return struct( 158 bzl = str(bzl), 159 rule = rule, 160 ) 161