1*d4726bddSHONG Yifan# Copyright 2020 Google 2*d4726bddSHONG Yifan# 3*d4726bddSHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License"); 4*d4726bddSHONG Yifan# you may not use this file except in compliance with the License. 5*d4726bddSHONG Yifan# You may obtain a copy of the License at 6*d4726bddSHONG Yifan# 7*d4726bddSHONG Yifan# http://www.apache.org/licenses/LICENSE-2.0 8*d4726bddSHONG Yifan# 9*d4726bddSHONG Yifan# Unless required by applicable law or agreed to in writing, software 10*d4726bddSHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS, 11*d4726bddSHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*d4726bddSHONG Yifan# See the License for the specific language governing permissions and 13*d4726bddSHONG Yifan# limitations under the License. 14*d4726bddSHONG Yifan 15*d4726bddSHONG Yifan""" 16*d4726bddSHONG YifanRust Analyzer Bazel rules. 17*d4726bddSHONG Yifan 18*d4726bddSHONG Yifanrust_analyzer will generate a rust-project.json file for the 19*d4726bddSHONG Yifangiven targets. This file can be consumed by rust-analyzer as an alternative 20*d4726bddSHONG Yifanto Cargo.toml files. 21*d4726bddSHONG Yifan""" 22*d4726bddSHONG Yifan 23*d4726bddSHONG Yifanload("//rust/platform:triple_mappings.bzl", "system_to_dylib_ext", "triple_to_system") 24*d4726bddSHONG Yifanload("//rust/private:common.bzl", "rust_common") 25*d4726bddSHONG Yifanload("//rust/private:providers.bzl", "RustAnalyzerGroupInfo", "RustAnalyzerInfo") 26*d4726bddSHONG Yifanload("//rust/private:rustc.bzl", "BuildInfo") 27*d4726bddSHONG Yifanload( 28*d4726bddSHONG Yifan "//rust/private:utils.bzl", 29*d4726bddSHONG Yifan "concat", 30*d4726bddSHONG Yifan "dedent", 31*d4726bddSHONG Yifan "dedup_expand_location", 32*d4726bddSHONG Yifan "find_toolchain", 33*d4726bddSHONG Yifan) 34*d4726bddSHONG Yifan 35*d4726bddSHONG Yifandef write_rust_analyzer_spec_file(ctx, attrs, owner, base_info): 36*d4726bddSHONG Yifan """Write a rust-analyzer spec info file. 37*d4726bddSHONG Yifan 38*d4726bddSHONG Yifan Args: 39*d4726bddSHONG Yifan ctx (ctx): The current rule's context object. 40*d4726bddSHONG Yifan attrs (dict): A mapping of attributes. 41*d4726bddSHONG Yifan owner (Label): The label of the owner of the spec info. 42*d4726bddSHONG Yifan base_info (RustAnalyzerInfo): The data the resulting RustAnalyzerInfo is based on. 43*d4726bddSHONG Yifan 44*d4726bddSHONG Yifan Returns: 45*d4726bddSHONG Yifan RustAnalyzerInfo: Info with the embedded spec file. 46*d4726bddSHONG Yifan """ 47*d4726bddSHONG Yifan crate_spec = ctx.actions.declare_file("{}.rust_analyzer_crate_spec.json".format(owner.name)) 48*d4726bddSHONG Yifan 49*d4726bddSHONG Yifan # Recreate the provider with the spec file embedded in it. 50*d4726bddSHONG Yifan rust_analyzer_info = RustAnalyzerInfo( 51*d4726bddSHONG Yifan aliases = base_info.aliases, 52*d4726bddSHONG Yifan crate = base_info.crate, 53*d4726bddSHONG Yifan cfgs = base_info.cfgs, 54*d4726bddSHONG Yifan env = base_info.env, 55*d4726bddSHONG Yifan deps = base_info.deps, 56*d4726bddSHONG Yifan crate_specs = depset(direct = [crate_spec], transitive = [base_info.crate_specs]), 57*d4726bddSHONG Yifan proc_macro_dylib_path = base_info.proc_macro_dylib_path, 58*d4726bddSHONG Yifan build_info = base_info.build_info, 59*d4726bddSHONG Yifan ) 60*d4726bddSHONG Yifan 61*d4726bddSHONG Yifan ctx.actions.write( 62*d4726bddSHONG Yifan output = crate_spec, 63*d4726bddSHONG Yifan content = json.encode_indent( 64*d4726bddSHONG Yifan _create_single_crate( 65*d4726bddSHONG Yifan ctx, 66*d4726bddSHONG Yifan attrs, 67*d4726bddSHONG Yifan rust_analyzer_info, 68*d4726bddSHONG Yifan ), 69*d4726bddSHONG Yifan indent = " " * 4, 70*d4726bddSHONG Yifan ), 71*d4726bddSHONG Yifan ) 72*d4726bddSHONG Yifan 73*d4726bddSHONG Yifan return rust_analyzer_info 74*d4726bddSHONG Yifan 75*d4726bddSHONG Yifandef _accumulate_rust_analyzer_info(dep_infos_to_accumulate, label_index_to_accumulate, dep): 76*d4726bddSHONG Yifan if dep == None: 77*d4726bddSHONG Yifan return 78*d4726bddSHONG Yifan if RustAnalyzerInfo in dep: 79*d4726bddSHONG Yifan label_index_to_accumulate[dep.label] = dep[RustAnalyzerInfo] 80*d4726bddSHONG Yifan dep_infos_to_accumulate.append(dep[RustAnalyzerInfo]) 81*d4726bddSHONG Yifan if RustAnalyzerGroupInfo in dep: 82*d4726bddSHONG Yifan for expanded_dep in dep[RustAnalyzerGroupInfo].deps: 83*d4726bddSHONG Yifan label_index_to_accumulate[expanded_dep.crate.owner] = expanded_dep 84*d4726bddSHONG Yifan dep_infos_to_accumulate.append(expanded_dep) 85*d4726bddSHONG Yifan 86*d4726bddSHONG Yifandef _accumulate_rust_analyzer_infos(dep_infos_to_accumulate, label_index_to_accumulate, deps_attr): 87*d4726bddSHONG Yifan for dep in deps_attr: 88*d4726bddSHONG Yifan _accumulate_rust_analyzer_info(dep_infos_to_accumulate, label_index_to_accumulate, dep) 89*d4726bddSHONG Yifan 90*d4726bddSHONG Yifandef _rust_analyzer_aspect_impl(target, ctx): 91*d4726bddSHONG Yifan if (rust_common.crate_info not in target and 92*d4726bddSHONG Yifan rust_common.test_crate_info not in target and 93*d4726bddSHONG Yifan rust_common.crate_group_info not in target): 94*d4726bddSHONG Yifan return [] 95*d4726bddSHONG Yifan 96*d4726bddSHONG Yifan if RustAnalyzerInfo in target or RustAnalyzerGroupInfo in target: 97*d4726bddSHONG Yifan return [] 98*d4726bddSHONG Yifan 99*d4726bddSHONG Yifan toolchain = find_toolchain(ctx) 100*d4726bddSHONG Yifan 101*d4726bddSHONG Yifan # Always add `test` & `debug_assertions`. See rust-analyzer source code: 102*d4726bddSHONG Yifan # https://github.com/rust-analyzer/rust-analyzer/blob/2021-11-15/crates/project_model/src/workspace.rs#L529-L531 103*d4726bddSHONG Yifan cfgs = ["test", "debug_assertions"] 104*d4726bddSHONG Yifan if hasattr(ctx.rule.attr, "crate_features"): 105*d4726bddSHONG Yifan cfgs += ['feature="{}"'.format(f) for f in ctx.rule.attr.crate_features] 106*d4726bddSHONG Yifan if hasattr(ctx.rule.attr, "rustc_flags"): 107*d4726bddSHONG Yifan cfgs += [f[6:] for f in ctx.rule.attr.rustc_flags if f.startswith("--cfg ") or f.startswith("--cfg=")] 108*d4726bddSHONG Yifan 109*d4726bddSHONG Yifan build_info = None 110*d4726bddSHONG Yifan dep_infos = [] 111*d4726bddSHONG Yifan labels_to_rais = {} 112*d4726bddSHONG Yifan 113*d4726bddSHONG Yifan for dep in getattr(ctx.rule.attr, "deps", []): 114*d4726bddSHONG Yifan # Save BuildInfo if we find any (for build script output) 115*d4726bddSHONG Yifan if BuildInfo in dep: 116*d4726bddSHONG Yifan build_info = dep[BuildInfo] 117*d4726bddSHONG Yifan 118*d4726bddSHONG Yifan _accumulate_rust_analyzer_infos(dep_infos, labels_to_rais, getattr(ctx.rule.attr, "deps", [])) 119*d4726bddSHONG Yifan _accumulate_rust_analyzer_infos(dep_infos, labels_to_rais, getattr(ctx.rule.attr, "proc_macro_deps", [])) 120*d4726bddSHONG Yifan 121*d4726bddSHONG Yifan _accumulate_rust_analyzer_info(dep_infos, labels_to_rais, getattr(ctx.rule.attr, "crate", None)) 122*d4726bddSHONG Yifan _accumulate_rust_analyzer_info(dep_infos, labels_to_rais, getattr(ctx.rule.attr, "actual", None)) 123*d4726bddSHONG Yifan 124*d4726bddSHONG Yifan if rust_common.crate_group_info in target: 125*d4726bddSHONG Yifan return [RustAnalyzerGroupInfo(deps = dep_infos)] 126*d4726bddSHONG Yifan elif rust_common.crate_info in target: 127*d4726bddSHONG Yifan crate_info = target[rust_common.crate_info] 128*d4726bddSHONG Yifan elif rust_common.test_crate_info in target: 129*d4726bddSHONG Yifan crate_info = target[rust_common.test_crate_info].crate 130*d4726bddSHONG Yifan else: 131*d4726bddSHONG Yifan fail("Unexpected target type: {}".format(target)) 132*d4726bddSHONG Yifan 133*d4726bddSHONG Yifan aliases = {} 134*d4726bddSHONG Yifan for aliased_target, aliased_name in getattr(ctx.rule.attr, "aliases", {}).items(): 135*d4726bddSHONG Yifan if aliased_target.label in labels_to_rais: 136*d4726bddSHONG Yifan aliases[labels_to_rais[aliased_target.label]] = aliased_name 137*d4726bddSHONG Yifan 138*d4726bddSHONG Yifan rust_analyzer_info = write_rust_analyzer_spec_file(ctx, ctx.rule.attr, ctx.label, RustAnalyzerInfo( 139*d4726bddSHONG Yifan aliases = aliases, 140*d4726bddSHONG Yifan crate = crate_info, 141*d4726bddSHONG Yifan cfgs = cfgs, 142*d4726bddSHONG Yifan env = crate_info.rustc_env, 143*d4726bddSHONG Yifan deps = dep_infos, 144*d4726bddSHONG Yifan crate_specs = depset(transitive = [dep.crate_specs for dep in dep_infos]), 145*d4726bddSHONG Yifan proc_macro_dylib_path = find_proc_macro_dylib_path(toolchain, target), 146*d4726bddSHONG Yifan build_info = build_info, 147*d4726bddSHONG Yifan )) 148*d4726bddSHONG Yifan 149*d4726bddSHONG Yifan return [ 150*d4726bddSHONG Yifan rust_analyzer_info, 151*d4726bddSHONG Yifan OutputGroupInfo(rust_analyzer_crate_spec = rust_analyzer_info.crate_specs), 152*d4726bddSHONG Yifan ] 153*d4726bddSHONG Yifan 154*d4726bddSHONG Yifandef find_proc_macro_dylib_path(toolchain, target): 155*d4726bddSHONG Yifan """Find the proc_macro_dylib_path of target. Returns None if target crate is not type proc-macro. 156*d4726bddSHONG Yifan 157*d4726bddSHONG Yifan Args: 158*d4726bddSHONG Yifan toolchain: The current rust toolchain. 159*d4726bddSHONG Yifan target: The current target. 160*d4726bddSHONG Yifan Returns: 161*d4726bddSHONG Yifan (path): The path to the proc macro dylib, or None if this crate is not a proc-macro. 162*d4726bddSHONG Yifan """ 163*d4726bddSHONG Yifan if rust_common.crate_info in target: 164*d4726bddSHONG Yifan crate_info = target[rust_common.crate_info] 165*d4726bddSHONG Yifan elif rust_common.test_crate_info in target: 166*d4726bddSHONG Yifan crate_info = target[rust_common.test_crate_info].crate 167*d4726bddSHONG Yifan else: 168*d4726bddSHONG Yifan return None 169*d4726bddSHONG Yifan 170*d4726bddSHONG Yifan if crate_info.type != "proc-macro": 171*d4726bddSHONG Yifan return None 172*d4726bddSHONG Yifan 173*d4726bddSHONG Yifan dylib_ext = system_to_dylib_ext(triple_to_system(toolchain.target_triple)) 174*d4726bddSHONG Yifan for action in target.actions: 175*d4726bddSHONG Yifan for output in action.outputs.to_list(): 176*d4726bddSHONG Yifan if output.extension == dylib_ext[1:]: 177*d4726bddSHONG Yifan return output.path 178*d4726bddSHONG Yifan 179*d4726bddSHONG Yifan # Failed to find the dylib path inside a proc-macro crate. 180*d4726bddSHONG Yifan # TODO: Should this be an error? 181*d4726bddSHONG Yifan return None 182*d4726bddSHONG Yifan 183*d4726bddSHONG Yifanrust_analyzer_aspect = aspect( 184*d4726bddSHONG Yifan attr_aspects = ["deps", "proc_macro_deps", "crate", "actual", "proto"], 185*d4726bddSHONG Yifan implementation = _rust_analyzer_aspect_impl, 186*d4726bddSHONG Yifan toolchains = [str(Label("//rust:toolchain_type"))], 187*d4726bddSHONG Yifan doc = "Annotates rust rules with RustAnalyzerInfo later used to build a rust-project.json", 188*d4726bddSHONG Yifan) 189*d4726bddSHONG Yifan 190*d4726bddSHONG Yifan_EXEC_ROOT_TEMPLATE = "__EXEC_ROOT__/" 191*d4726bddSHONG Yifan_OUTPUT_BASE_TEMPLATE = "__OUTPUT_BASE__/" 192*d4726bddSHONG Yifan 193*d4726bddSHONG Yifandef _crate_id(crate_info): 194*d4726bddSHONG Yifan """Returns a unique stable identifier for a crate 195*d4726bddSHONG Yifan 196*d4726bddSHONG Yifan Returns: 197*d4726bddSHONG Yifan (string): This crate's unique stable id. 198*d4726bddSHONG Yifan """ 199*d4726bddSHONG Yifan return "ID-" + crate_info.root.path 200*d4726bddSHONG Yifan 201*d4726bddSHONG Yifandef _create_single_crate(ctx, attrs, info): 202*d4726bddSHONG Yifan """Creates a crate in the rust-project.json format. 203*d4726bddSHONG Yifan 204*d4726bddSHONG Yifan Args: 205*d4726bddSHONG Yifan ctx (ctx): The rule context. 206*d4726bddSHONG Yifan attrs (dict): A mapping of attributes. 207*d4726bddSHONG Yifan info (RustAnalyzerInfo): RustAnalyzerInfo for the current crate. 208*d4726bddSHONG Yifan 209*d4726bddSHONG Yifan Returns: 210*d4726bddSHONG Yifan (dict) The crate rust-project.json representation 211*d4726bddSHONG Yifan """ 212*d4726bddSHONG Yifan crate_name = info.crate.name 213*d4726bddSHONG Yifan crate = dict() 214*d4726bddSHONG Yifan crate_id = _crate_id(info.crate) 215*d4726bddSHONG Yifan crate["crate_id"] = crate_id 216*d4726bddSHONG Yifan crate["display_name"] = crate_name 217*d4726bddSHONG Yifan crate["edition"] = info.crate.edition 218*d4726bddSHONG Yifan crate["env"] = {} 219*d4726bddSHONG Yifan crate["crate_type"] = info.crate.type 220*d4726bddSHONG Yifan 221*d4726bddSHONG Yifan # Switch on external/ to determine if crates are in the workspace or remote. 222*d4726bddSHONG Yifan # TODO: Some folks may want to override this for vendored dependencies. 223*d4726bddSHONG Yifan is_external = info.crate.root.path.startswith("external/") 224*d4726bddSHONG Yifan is_generated = not info.crate.root.is_source 225*d4726bddSHONG Yifan path_prefix = _EXEC_ROOT_TEMPLATE if is_external or is_generated else "" 226*d4726bddSHONG Yifan crate["is_workspace_member"] = not is_external 227*d4726bddSHONG Yifan crate["root_module"] = path_prefix + info.crate.root.path 228*d4726bddSHONG Yifan crate["source"] = {"exclude_dirs": [], "include_dirs": []} 229*d4726bddSHONG Yifan 230*d4726bddSHONG Yifan if is_generated: 231*d4726bddSHONG Yifan srcs = getattr(ctx.rule.files, "srcs", []) 232*d4726bddSHONG Yifan src_map = {src.short_path: src for src in srcs if src.is_source} 233*d4726bddSHONG Yifan if info.crate.root.short_path in src_map: 234*d4726bddSHONG Yifan crate["root_module"] = src_map[info.crate.root.short_path].path 235*d4726bddSHONG Yifan crate["source"]["include_dirs"].append(path_prefix + info.crate.root.dirname) 236*d4726bddSHONG Yifan 237*d4726bddSHONG Yifan if info.build_info != None and info.build_info.out_dir != None: 238*d4726bddSHONG Yifan out_dir_path = info.build_info.out_dir.path 239*d4726bddSHONG Yifan crate["env"].update({"OUT_DIR": _EXEC_ROOT_TEMPLATE + out_dir_path}) 240*d4726bddSHONG Yifan 241*d4726bddSHONG Yifan # We have to tell rust-analyzer about our out_dir since it's not under the crate root. 242*d4726bddSHONG Yifan crate["source"]["include_dirs"].extend([ 243*d4726bddSHONG Yifan path_prefix + info.crate.root.dirname, 244*d4726bddSHONG Yifan _EXEC_ROOT_TEMPLATE + out_dir_path, 245*d4726bddSHONG Yifan ]) 246*d4726bddSHONG Yifan 247*d4726bddSHONG Yifan # TODO: The only imagined use case is an env var holding a filename in the workspace passed to a 248*d4726bddSHONG Yifan # macro like include_bytes!. Other use cases might exist that require more complex logic. 249*d4726bddSHONG Yifan expand_targets = concat([getattr(attrs, attr, []) for attr in ["data", "compile_data"]]) 250*d4726bddSHONG Yifan 251*d4726bddSHONG Yifan crate["env"].update({k: dedup_expand_location(ctx, v, expand_targets) for k, v in info.env.items()}) 252*d4726bddSHONG Yifan 253*d4726bddSHONG Yifan # Omit when a crate appears to depend on itself (e.g. foo_test crates). 254*d4726bddSHONG Yifan # It can happen a single source file is present in multiple crates - there can 255*d4726bddSHONG Yifan # be a `rust_library` with a `lib.rs` file, and a `rust_test` for the `test` 256*d4726bddSHONG Yifan # module in that file. Tests can declare more dependencies than what library 257*d4726bddSHONG Yifan # had. Therefore we had to collect all RustAnalyzerInfos for a given crate 258*d4726bddSHONG Yifan # and take deps from all of them. 259*d4726bddSHONG Yifan 260*d4726bddSHONG Yifan # There's one exception - if the dependency is the same crate name as the 261*d4726bddSHONG Yifan # the crate being processed, we don't add it as a dependency to itself. This is 262*d4726bddSHONG Yifan # common and expected - `rust_test.crate` pointing to the `rust_library`. 263*d4726bddSHONG Yifan crate["deps"] = [_crate_id(dep.crate) for dep in info.deps if _crate_id(dep.crate) != crate_id] 264*d4726bddSHONG Yifan crate["aliases"] = {_crate_id(alias_target.crate): alias_name for alias_target, alias_name in info.aliases.items()} 265*d4726bddSHONG Yifan crate["cfg"] = info.cfgs 266*d4726bddSHONG Yifan crate["target"] = find_toolchain(ctx).target_triple.str 267*d4726bddSHONG Yifan if info.proc_macro_dylib_path != None: 268*d4726bddSHONG Yifan crate["proc_macro_dylib_path"] = _EXEC_ROOT_TEMPLATE + info.proc_macro_dylib_path 269*d4726bddSHONG Yifan return crate 270*d4726bddSHONG Yifan 271*d4726bddSHONG Yifandef _rust_analyzer_toolchain_impl(ctx): 272*d4726bddSHONG Yifan toolchain = platform_common.ToolchainInfo( 273*d4726bddSHONG Yifan proc_macro_srv = ctx.executable.proc_macro_srv, 274*d4726bddSHONG Yifan rustc = ctx.executable.rustc, 275*d4726bddSHONG Yifan rustc_srcs = ctx.attr.rustc_srcs, 276*d4726bddSHONG Yifan ) 277*d4726bddSHONG Yifan 278*d4726bddSHONG Yifan return [toolchain] 279*d4726bddSHONG Yifan 280*d4726bddSHONG Yifanrust_analyzer_toolchain = rule( 281*d4726bddSHONG Yifan implementation = _rust_analyzer_toolchain_impl, 282*d4726bddSHONG Yifan doc = "A toolchain for [rust-analyzer](https://rust-analyzer.github.io/).", 283*d4726bddSHONG Yifan attrs = { 284*d4726bddSHONG Yifan "proc_macro_srv": attr.label( 285*d4726bddSHONG Yifan doc = "The path to a `rust_analyzer_proc_macro_srv` binary.", 286*d4726bddSHONG Yifan cfg = "exec", 287*d4726bddSHONG Yifan executable = True, 288*d4726bddSHONG Yifan allow_single_file = True, 289*d4726bddSHONG Yifan ), 290*d4726bddSHONG Yifan "rustc": attr.label( 291*d4726bddSHONG Yifan doc = "The path to a `rustc` binary.", 292*d4726bddSHONG Yifan cfg = "exec", 293*d4726bddSHONG Yifan executable = True, 294*d4726bddSHONG Yifan allow_single_file = True, 295*d4726bddSHONG Yifan mandatory = True, 296*d4726bddSHONG Yifan ), 297*d4726bddSHONG Yifan "rustc_srcs": attr.label( 298*d4726bddSHONG Yifan doc = "The source code of rustc.", 299*d4726bddSHONG Yifan mandatory = True, 300*d4726bddSHONG Yifan ), 301*d4726bddSHONG Yifan }, 302*d4726bddSHONG Yifan) 303*d4726bddSHONG Yifan 304*d4726bddSHONG Yifandef _rust_analyzer_detect_sysroot_impl(ctx): 305*d4726bddSHONG Yifan rust_analyzer_toolchain = ctx.toolchains[Label("@rules_rust//rust/rust_analyzer:toolchain_type")] 306*d4726bddSHONG Yifan 307*d4726bddSHONG Yifan if not rust_analyzer_toolchain.rustc_srcs: 308*d4726bddSHONG Yifan fail( 309*d4726bddSHONG Yifan "Current Rust-Analyzer toolchain doesn't contain rustc sources in `rustc_srcs` attribute.", 310*d4726bddSHONG Yifan "These are needed by rust-analyzer. If you are using the default Rust toolchain, add `rust_repositories(include_rustc_srcs = True, ...).` to your WORKSPACE file.", 311*d4726bddSHONG Yifan ) 312*d4726bddSHONG Yifan 313*d4726bddSHONG Yifan rustc_srcs = rust_analyzer_toolchain.rustc_srcs 314*d4726bddSHONG Yifan 315*d4726bddSHONG Yifan sysroot_src = rustc_srcs.label.package + "/library" 316*d4726bddSHONG Yifan if rustc_srcs.label.workspace_root: 317*d4726bddSHONG Yifan sysroot_src = _OUTPUT_BASE_TEMPLATE + rustc_srcs.label.workspace_root + "/" + sysroot_src 318*d4726bddSHONG Yifan 319*d4726bddSHONG Yifan rustc = rust_analyzer_toolchain.rustc 320*d4726bddSHONG Yifan sysroot_dir, _, bin_dir = rustc.dirname.rpartition("/") 321*d4726bddSHONG Yifan if bin_dir != "bin": 322*d4726bddSHONG Yifan fail("The rustc path is expected to be relative to the sysroot as `bin/rustc`. Instead got: {}".format( 323*d4726bddSHONG Yifan rustc.path, 324*d4726bddSHONG Yifan )) 325*d4726bddSHONG Yifan 326*d4726bddSHONG Yifan sysroot = "{}/{}".format( 327*d4726bddSHONG Yifan _OUTPUT_BASE_TEMPLATE, 328*d4726bddSHONG Yifan sysroot_dir, 329*d4726bddSHONG Yifan ) 330*d4726bddSHONG Yifan 331*d4726bddSHONG Yifan toolchain_info = { 332*d4726bddSHONG Yifan "sysroot": sysroot, 333*d4726bddSHONG Yifan "sysroot_src": sysroot_src, 334*d4726bddSHONG Yifan } 335*d4726bddSHONG Yifan 336*d4726bddSHONG Yifan output = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_toolchain.json") 337*d4726bddSHONG Yifan ctx.actions.write( 338*d4726bddSHONG Yifan output = output, 339*d4726bddSHONG Yifan content = json.encode_indent(toolchain_info, indent = " " * 4), 340*d4726bddSHONG Yifan ) 341*d4726bddSHONG Yifan 342*d4726bddSHONG Yifan return [DefaultInfo(files = depset([output]))] 343*d4726bddSHONG Yifan 344*d4726bddSHONG Yifanrust_analyzer_detect_sysroot = rule( 345*d4726bddSHONG Yifan implementation = _rust_analyzer_detect_sysroot_impl, 346*d4726bddSHONG Yifan toolchains = [ 347*d4726bddSHONG Yifan "@rules_rust//rust:toolchain_type", 348*d4726bddSHONG Yifan "@rules_rust//rust/rust_analyzer:toolchain_type", 349*d4726bddSHONG Yifan ], 350*d4726bddSHONG Yifan doc = dedent("""\ 351*d4726bddSHONG Yifan Detect the sysroot and store in a file for use by the gen_rust_project tool. 352*d4726bddSHONG Yifan """), 353*d4726bddSHONG Yifan) 354