1*d4726bddSHONG Yifan"""Module extension for generating third-party crates for use in bazel.""" 2*d4726bddSHONG Yifan 3*d4726bddSHONG Yifanload("@bazel_features//:features.bzl", "bazel_features") 4*d4726bddSHONG Yifanload("@bazel_skylib//lib:structs.bzl", "structs") 5*d4726bddSHONG Yifanload("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") 6*d4726bddSHONG Yifanload("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 7*d4726bddSHONG Yifanload("//crate_universe:defs.bzl", _crate_universe_crate = "crate") 8*d4726bddSHONG Yifanload("//crate_universe/private:crates_vendor.bzl", "CRATES_VENDOR_ATTRS", "generate_config_file", "generate_splicing_manifest") 9*d4726bddSHONG Yifanload("//crate_universe/private:generate_utils.bzl", "CARGO_BAZEL_GENERATOR_SHA256", "CARGO_BAZEL_GENERATOR_URL", "GENERATOR_ENV_VARS", "render_config") 10*d4726bddSHONG Yifanload("//crate_universe/private:urls.bzl", "CARGO_BAZEL_SHA256S", "CARGO_BAZEL_URLS") 11*d4726bddSHONG Yifanload("//crate_universe/private/module_extensions:cargo_bazel_bootstrap.bzl", "get_cargo_bazel_runner", "get_host_cargo_rustc") 12*d4726bddSHONG Yifanload("//rust/platform:triple.bzl", "get_host_triple") 13*d4726bddSHONG Yifan 14*d4726bddSHONG Yifan# A list of labels which may be relative (and if so, is within the repo the rule is generated in). 15*d4726bddSHONG Yifan# 16*d4726bddSHONG Yifan# If I were to write ":foo", with attr.label_list, it would evaluate to 17*d4726bddSHONG Yifan# "@@//:foo". However, for a tag such as deps, ":foo" should refer to 18*d4726bddSHONG Yifan# "@@rules_rust~crates~<crate>//:foo". 19*d4726bddSHONG Yifan_relative_label_list = attr.string_list 20*d4726bddSHONG Yifan 21*d4726bddSHONG Yifan_OPT_BOOL_VALUES = { 22*d4726bddSHONG Yifan "auto": None, 23*d4726bddSHONG Yifan "off": False, 24*d4726bddSHONG Yifan "on": True, 25*d4726bddSHONG Yifan} 26*d4726bddSHONG Yifan 27*d4726bddSHONG Yifandef optional_bool(doc): 28*d4726bddSHONG Yifan return attr.string( 29*d4726bddSHONG Yifan doc = doc, 30*d4726bddSHONG Yifan values = _OPT_BOOL_VALUES.keys(), 31*d4726bddSHONG Yifan default = "auto", 32*d4726bddSHONG Yifan ) 33*d4726bddSHONG Yifan 34*d4726bddSHONG Yifandef _get_or_insert(d, key, value): 35*d4726bddSHONG Yifan if key not in d: 36*d4726bddSHONG Yifan d[key] = value 37*d4726bddSHONG Yifan return d[key] 38*d4726bddSHONG Yifan 39*d4726bddSHONG Yifandef _generate_repo_impl(repo_ctx): 40*d4726bddSHONG Yifan for path, contents in repo_ctx.attr.contents.items(): 41*d4726bddSHONG Yifan repo_ctx.file(path, contents) 42*d4726bddSHONG Yifan 43*d4726bddSHONG Yifan_generate_repo = repository_rule( 44*d4726bddSHONG Yifan implementation = _generate_repo_impl, 45*d4726bddSHONG Yifan attrs = dict( 46*d4726bddSHONG Yifan contents = attr.string_dict(mandatory = True), 47*d4726bddSHONG Yifan ), 48*d4726bddSHONG Yifan) 49*d4726bddSHONG Yifan 50*d4726bddSHONG Yifandef _annotations_for_repo(module_annotations, repo_specific_annotations): 51*d4726bddSHONG Yifan """Merges the set of global annotations with the repo-specific ones 52*d4726bddSHONG Yifan 53*d4726bddSHONG Yifan Args: 54*d4726bddSHONG Yifan module_annotations (dict): The annotation tags that apply to all repos, keyed by crate. 55*d4726bddSHONG Yifan repo_specific_annotations (dict): The annotation tags that apply to only this repo, keyed by crate. 56*d4726bddSHONG Yifan """ 57*d4726bddSHONG Yifan 58*d4726bddSHONG Yifan if not repo_specific_annotations: 59*d4726bddSHONG Yifan return module_annotations 60*d4726bddSHONG Yifan 61*d4726bddSHONG Yifan annotations = dict(module_annotations) 62*d4726bddSHONG Yifan for crate, values in repo_specific_annotations.items(): 63*d4726bddSHONG Yifan _get_or_insert(annotations, crate, []).extend(values) 64*d4726bddSHONG Yifan return annotations 65*d4726bddSHONG Yifan 66*d4726bddSHONG Yifandef _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, cargo_lockfile = None, manifests = {}, packages = {}): 67*d4726bddSHONG Yifan """Generates repositories for the transitive closure of crates defined by manifests and packages. 68*d4726bddSHONG Yifan 69*d4726bddSHONG Yifan Args: 70*d4726bddSHONG Yifan module_ctx (module_ctx): The module context object. 71*d4726bddSHONG Yifan cargo_bazel (function): A function that can be called to execute cargo_bazel. 72*d4726bddSHONG Yifan cfg (object): The module tag from `from_cargo` or `from_specs` 73*d4726bddSHONG Yifan annotations (dict): The set of annotation tag classes that apply to this closure, keyed by crate name. 74*d4726bddSHONG Yifan cargo_lockfile (path): Path to Cargo.lock, if we have one. This is optional for `from_specs` closures. 75*d4726bddSHONG Yifan manifests (dict): The set of Cargo.toml manifests that apply to this closure, if any, keyed by path. 76*d4726bddSHONG Yifan packages (dict): The set of extra cargo crate tags that apply to this closure, if any, keyed by package name. 77*d4726bddSHONG Yifan """ 78*d4726bddSHONG Yifan 79*d4726bddSHONG Yifan tag_path = module_ctx.path(cfg.name) 80*d4726bddSHONG Yifan 81*d4726bddSHONG Yifan rendering_config = json.decode(render_config( 82*d4726bddSHONG Yifan regen_command = "Run 'cargo update [--workspace]'", 83*d4726bddSHONG Yifan )) 84*d4726bddSHONG Yifan config_file = tag_path.get_child("config.json") 85*d4726bddSHONG Yifan module_ctx.file( 86*d4726bddSHONG Yifan config_file, 87*d4726bddSHONG Yifan executable = False, 88*d4726bddSHONG Yifan content = generate_config_file( 89*d4726bddSHONG Yifan module_ctx, 90*d4726bddSHONG Yifan mode = "remote", 91*d4726bddSHONG Yifan annotations = annotations, 92*d4726bddSHONG Yifan generate_build_scripts = cfg.generate_build_scripts, 93*d4726bddSHONG Yifan supported_platform_triples = cfg.supported_platform_triples, 94*d4726bddSHONG Yifan generate_target_compatible_with = True, 95*d4726bddSHONG Yifan repository_name = cfg.name, 96*d4726bddSHONG Yifan output_pkg = cfg.name, 97*d4726bddSHONG Yifan workspace_name = cfg.name, 98*d4726bddSHONG Yifan generate_binaries = cfg.generate_binaries, 99*d4726bddSHONG Yifan render_config = rendering_config, 100*d4726bddSHONG Yifan repository_ctx = module_ctx, 101*d4726bddSHONG Yifan ), 102*d4726bddSHONG Yifan ) 103*d4726bddSHONG Yifan 104*d4726bddSHONG Yifan splicing_manifest = tag_path.get_child("splicing_manifest.json") 105*d4726bddSHONG Yifan module_ctx.file( 106*d4726bddSHONG Yifan splicing_manifest, 107*d4726bddSHONG Yifan executable = False, 108*d4726bddSHONG Yifan content = generate_splicing_manifest( 109*d4726bddSHONG Yifan packages = packages, 110*d4726bddSHONG Yifan splicing_config = "", 111*d4726bddSHONG Yifan cargo_config = cfg.cargo_config, 112*d4726bddSHONG Yifan manifests = manifests, 113*d4726bddSHONG Yifan manifest_to_path = module_ctx.path, 114*d4726bddSHONG Yifan ), 115*d4726bddSHONG Yifan ) 116*d4726bddSHONG Yifan 117*d4726bddSHONG Yifan splicing_output_dir = tag_path.get_child("splicing-output") 118*d4726bddSHONG Yifan splice_args = [ 119*d4726bddSHONG Yifan "splice", 120*d4726bddSHONG Yifan "--output-dir", 121*d4726bddSHONG Yifan splicing_output_dir, 122*d4726bddSHONG Yifan "--config", 123*d4726bddSHONG Yifan config_file, 124*d4726bddSHONG Yifan "--splicing-manifest", 125*d4726bddSHONG Yifan splicing_manifest, 126*d4726bddSHONG Yifan ] 127*d4726bddSHONG Yifan if cargo_lockfile: 128*d4726bddSHONG Yifan splice_args.extend([ 129*d4726bddSHONG Yifan "--cargo-lockfile", 130*d4726bddSHONG Yifan cargo_lockfile, 131*d4726bddSHONG Yifan ]) 132*d4726bddSHONG Yifan cargo_bazel(splice_args) 133*d4726bddSHONG Yifan 134*d4726bddSHONG Yifan # Create a lockfile, since we need to parse it to generate spoke 135*d4726bddSHONG Yifan # repos. 136*d4726bddSHONG Yifan lockfile_path = tag_path.get_child("lockfile.json") 137*d4726bddSHONG Yifan module_ctx.file(lockfile_path, "") 138*d4726bddSHONG Yifan 139*d4726bddSHONG Yifan cargo_bazel([ 140*d4726bddSHONG Yifan "generate", 141*d4726bddSHONG Yifan "--cargo-lockfile", 142*d4726bddSHONG Yifan cargo_lockfile or splicing_output_dir.get_child("Cargo.lock"), 143*d4726bddSHONG Yifan "--config", 144*d4726bddSHONG Yifan config_file, 145*d4726bddSHONG Yifan "--splicing-manifest", 146*d4726bddSHONG Yifan splicing_manifest, 147*d4726bddSHONG Yifan "--repository-dir", 148*d4726bddSHONG Yifan tag_path, 149*d4726bddSHONG Yifan "--metadata", 150*d4726bddSHONG Yifan splicing_output_dir.get_child("metadata.json"), 151*d4726bddSHONG Yifan "--repin", 152*d4726bddSHONG Yifan "--lockfile", 153*d4726bddSHONG Yifan lockfile_path, 154*d4726bddSHONG Yifan ]) 155*d4726bddSHONG Yifan 156*d4726bddSHONG Yifan crates_dir = tag_path.get_child(cfg.name) 157*d4726bddSHONG Yifan _generate_repo( 158*d4726bddSHONG Yifan name = cfg.name, 159*d4726bddSHONG Yifan contents = { 160*d4726bddSHONG Yifan "BUILD.bazel": module_ctx.read(crates_dir.get_child("BUILD.bazel")), 161*d4726bddSHONG Yifan "defs.bzl": module_ctx.read(crates_dir.get_child("defs.bzl")), 162*d4726bddSHONG Yifan }, 163*d4726bddSHONG Yifan ) 164*d4726bddSHONG Yifan 165*d4726bddSHONG Yifan contents = json.decode(module_ctx.read(lockfile_path)) 166*d4726bddSHONG Yifan 167*d4726bddSHONG Yifan for crate in contents["crates"].values(): 168*d4726bddSHONG Yifan repo = crate["repository"] 169*d4726bddSHONG Yifan if repo == None: 170*d4726bddSHONG Yifan continue 171*d4726bddSHONG Yifan name = crate["name"] 172*d4726bddSHONG Yifan version = crate["version"] 173*d4726bddSHONG Yifan 174*d4726bddSHONG Yifan # "+" isn't valid in a repo name. 175*d4726bddSHONG Yifan crate_repo_name = "{repo_name}__{name}-{version}".format( 176*d4726bddSHONG Yifan repo_name = cfg.name, 177*d4726bddSHONG Yifan name = name, 178*d4726bddSHONG Yifan version = version.replace("+", "-"), 179*d4726bddSHONG Yifan ) 180*d4726bddSHONG Yifan 181*d4726bddSHONG Yifan build_file_content = module_ctx.read(crates_dir.get_child("BUILD.%s-%s.bazel" % (name, version))) 182*d4726bddSHONG Yifan if "Http" in repo: 183*d4726bddSHONG Yifan # Replicates functionality in repo_http.j2. 184*d4726bddSHONG Yifan repo = repo["Http"] 185*d4726bddSHONG Yifan http_archive( 186*d4726bddSHONG Yifan name = crate_repo_name, 187*d4726bddSHONG Yifan patch_args = repo.get("patch_args", None), 188*d4726bddSHONG Yifan patch_tool = repo.get("patch_tool", None), 189*d4726bddSHONG Yifan patches = repo.get("patches", None), 190*d4726bddSHONG Yifan remote_patch_strip = 1, 191*d4726bddSHONG Yifan sha256 = repo.get("sha256", None), 192*d4726bddSHONG Yifan type = "tar.gz", 193*d4726bddSHONG Yifan urls = [repo["url"]], 194*d4726bddSHONG Yifan strip_prefix = "%s-%s" % (crate["name"], crate["version"]), 195*d4726bddSHONG Yifan build_file_content = build_file_content, 196*d4726bddSHONG Yifan ) 197*d4726bddSHONG Yifan elif "Git" in repo: 198*d4726bddSHONG Yifan # Replicates functionality in repo_git.j2 199*d4726bddSHONG Yifan repo = repo["Git"] 200*d4726bddSHONG Yifan kwargs = {} 201*d4726bddSHONG Yifan for k, v in repo["commitish"].items(): 202*d4726bddSHONG Yifan if k == "Rev": 203*d4726bddSHONG Yifan kwargs["commit"] = v 204*d4726bddSHONG Yifan else: 205*d4726bddSHONG Yifan kwargs[k.lower()] = v 206*d4726bddSHONG Yifan new_git_repository( 207*d4726bddSHONG Yifan name = crate_repo_name, 208*d4726bddSHONG Yifan init_submodules = True, 209*d4726bddSHONG Yifan patch_args = repo.get("patch_args", None), 210*d4726bddSHONG Yifan patch_tool = repo.get("patch_tool", None), 211*d4726bddSHONG Yifan patches = repo.get("patches", None), 212*d4726bddSHONG Yifan shallow_since = repo.get("shallow_since", None), 213*d4726bddSHONG Yifan remote = repo["remote"], 214*d4726bddSHONG Yifan build_file_content = build_file_content, 215*d4726bddSHONG Yifan strip_prefix = repo.get("strip_prefix", None), 216*d4726bddSHONG Yifan **kwargs 217*d4726bddSHONG Yifan ) 218*d4726bddSHONG Yifan else: 219*d4726bddSHONG Yifan fail("Invalid repo: expected Http or Git to exist for crate %s-%s, got %s" % (name, version, repo)) 220*d4726bddSHONG Yifan 221*d4726bddSHONG Yifandef _package_to_json(p): 222*d4726bddSHONG Yifan # Avoid adding unspecified properties. 223*d4726bddSHONG Yifan # If we add them as empty strings, cargo-bazel will be unhappy. 224*d4726bddSHONG Yifan return json.encode({ 225*d4726bddSHONG Yifan k: v 226*d4726bddSHONG Yifan for k, v in structs.to_dict(p).items() 227*d4726bddSHONG Yifan if v or k == "default_features" 228*d4726bddSHONG Yifan }) 229*d4726bddSHONG Yifan 230*d4726bddSHONG Yifandef _get_generator(module_ctx): 231*d4726bddSHONG Yifan """Query Network Resources to local a `cargo-bazel` binary. 232*d4726bddSHONG Yifan 233*d4726bddSHONG Yifan Based off get_generator in crates_universe/private/generate_utils.bzl 234*d4726bddSHONG Yifan 235*d4726bddSHONG Yifan Args: 236*d4726bddSHONG Yifan module_ctx (module_ctx): The rules context object 237*d4726bddSHONG Yifan 238*d4726bddSHONG Yifan Returns: 239*d4726bddSHONG Yifan tuple(path, dict) The path to a 'cargo-bazel' binary. The pairing (dict) 240*d4726bddSHONG Yifan may be `None` if there is not need to update the attribute 241*d4726bddSHONG Yifan """ 242*d4726bddSHONG Yifan host_triple = get_host_triple(module_ctx) 243*d4726bddSHONG Yifan use_environ = False 244*d4726bddSHONG Yifan for var in GENERATOR_ENV_VARS: 245*d4726bddSHONG Yifan if var in module_ctx.os.environ: 246*d4726bddSHONG Yifan use_environ = True 247*d4726bddSHONG Yifan 248*d4726bddSHONG Yifan if use_environ: 249*d4726bddSHONG Yifan generator_sha256 = module_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_SHA256) 250*d4726bddSHONG Yifan generator_url = module_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_URL) 251*d4726bddSHONG Yifan elif len(CARGO_BAZEL_URLS) == 0: 252*d4726bddSHONG Yifan return module_ctx.path(Label("@cargo_bazel_bootstrap//:cargo-bazel")) 253*d4726bddSHONG Yifan else: 254*d4726bddSHONG Yifan generator_sha256 = CARGO_BAZEL_SHA256S.get(host_triple.str) 255*d4726bddSHONG Yifan generator_url = CARGO_BAZEL_URLS.get(host_triple.str) 256*d4726bddSHONG Yifan 257*d4726bddSHONG Yifan if not generator_url: 258*d4726bddSHONG Yifan fail(( 259*d4726bddSHONG Yifan "No generator URL was found either in the `CARGO_BAZEL_GENERATOR_URL` " + 260*d4726bddSHONG Yifan "environment variable or for the `{}` triple in the `generator_urls` attribute" 261*d4726bddSHONG Yifan ).format(host_triple.str)) 262*d4726bddSHONG Yifan 263*d4726bddSHONG Yifan output = module_ctx.path("cargo-bazel.exe" if "win" in module_ctx.os.name else "cargo-bazel") 264*d4726bddSHONG Yifan 265*d4726bddSHONG Yifan # Download the file into place 266*d4726bddSHONG Yifan download_kwargs = { 267*d4726bddSHONG Yifan "executable": True, 268*d4726bddSHONG Yifan "output": output, 269*d4726bddSHONG Yifan "url": generator_url, 270*d4726bddSHONG Yifan } 271*d4726bddSHONG Yifan 272*d4726bddSHONG Yifan if generator_sha256: 273*d4726bddSHONG Yifan download_kwargs.update({"sha256": generator_sha256}) 274*d4726bddSHONG Yifan 275*d4726bddSHONG Yifan module_ctx.download(**download_kwargs) 276*d4726bddSHONG Yifan return output 277*d4726bddSHONG Yifan 278*d4726bddSHONG Yifandef _crate_impl(module_ctx): 279*d4726bddSHONG Yifan # Preload all external repositories. Calling `module_ctx.path` will cause restarts of the implementation 280*d4726bddSHONG Yifan # function of the module extension, so we want to trigger all restarts before we start the actual work. 281*d4726bddSHONG Yifan # Once https://github.com/bazelbuild/bazel/issues/22729 has been fixed, this code can be removed. 282*d4726bddSHONG Yifan get_host_cargo_rustc(module_ctx) 283*d4726bddSHONG Yifan for mod in module_ctx.modules: 284*d4726bddSHONG Yifan for cfg in mod.tags.from_cargo: 285*d4726bddSHONG Yifan module_ctx.path(cfg.cargo_lockfile) 286*d4726bddSHONG Yifan for m in cfg.manifests: 287*d4726bddSHONG Yifan module_ctx.path(m) 288*d4726bddSHONG Yifan 289*d4726bddSHONG Yifan cargo_bazel_output = _get_generator(module_ctx) 290*d4726bddSHONG Yifan cargo_bazel = get_cargo_bazel_runner(module_ctx, cargo_bazel_output) 291*d4726bddSHONG Yifan 292*d4726bddSHONG Yifan all_repos = [] 293*d4726bddSHONG Yifan reproducible = True 294*d4726bddSHONG Yifan 295*d4726bddSHONG Yifan for mod in module_ctx.modules: 296*d4726bddSHONG Yifan module_annotations = {} 297*d4726bddSHONG Yifan repo_specific_annotations = {} 298*d4726bddSHONG Yifan for annotation_tag in mod.tags.annotation: 299*d4726bddSHONG Yifan annotation_dict = structs.to_dict(annotation_tag) 300*d4726bddSHONG Yifan repositories = annotation_dict.pop("repositories") 301*d4726bddSHONG Yifan crate = annotation_dict.pop("crate") 302*d4726bddSHONG Yifan 303*d4726bddSHONG Yifan # The crate.annotation function can take in either a list or a bool. 304*d4726bddSHONG Yifan # For the tag-based method, because it has type safety, we have to 305*d4726bddSHONG Yifan # split it into two parameters. 306*d4726bddSHONG Yifan if annotation_dict.pop("gen_all_binaries"): 307*d4726bddSHONG Yifan annotation_dict["gen_binaries"] = True 308*d4726bddSHONG Yifan annotation_dict["gen_build_script"] = _OPT_BOOL_VALUES[annotation_dict["gen_build_script"]] 309*d4726bddSHONG Yifan 310*d4726bddSHONG Yifan # Process the override targets for the annotation. 311*d4726bddSHONG Yifan # In the non-bzlmod approach, this is given as a dict 312*d4726bddSHONG Yifan # with the possible keys "`proc_macro`, `build_script`, `lib`, `bin`". 313*d4726bddSHONG Yifan # With the tag-based approach used in Bzlmod, we run into an issue 314*d4726bddSHONG Yifan # where there is no dict type that takes a string as a key and a Label as the value. 315*d4726bddSHONG Yifan # To work around this, we split the override option into four, and reconstruct the 316*d4726bddSHONG Yifan # dictionary here during processing 317*d4726bddSHONG Yifan annotation_dict["override_targets"] = dict() 318*d4726bddSHONG Yifan replacement = annotation_dict.pop("override_target_lib") 319*d4726bddSHONG Yifan if replacement: 320*d4726bddSHONG Yifan annotation_dict["override_targets"]["lib"] = str(replacement) 321*d4726bddSHONG Yifan 322*d4726bddSHONG Yifan replacement = annotation_dict.pop("override_target_proc_macro") 323*d4726bddSHONG Yifan if replacement: 324*d4726bddSHONG Yifan annotation_dict["override_targets"]["proc_macro"] = str(replacement) 325*d4726bddSHONG Yifan 326*d4726bddSHONG Yifan replacement = annotation_dict.pop("override_target_build_script") 327*d4726bddSHONG Yifan if replacement: 328*d4726bddSHONG Yifan annotation_dict["override_targets"]["build_script"] = str(replacement) 329*d4726bddSHONG Yifan 330*d4726bddSHONG Yifan replacement = annotation_dict.pop("override_target_bin") 331*d4726bddSHONG Yifan if replacement: 332*d4726bddSHONG Yifan annotation_dict["override_targets"]["bin"] = str(replacement) 333*d4726bddSHONG Yifan 334*d4726bddSHONG Yifan annotation = _crate_universe_crate.annotation(**{ 335*d4726bddSHONG Yifan k: v 336*d4726bddSHONG Yifan for k, v in annotation_dict.items() 337*d4726bddSHONG Yifan # Tag classes can't take in None, but the function requires None 338*d4726bddSHONG Yifan # instead of the empty values in many cases. 339*d4726bddSHONG Yifan # https://github.com/bazelbuild/bazel/issues/20744 340*d4726bddSHONG Yifan if v != "" and v != [] and v != {} 341*d4726bddSHONG Yifan }) 342*d4726bddSHONG Yifan if not repositories: 343*d4726bddSHONG Yifan _get_or_insert(module_annotations, crate, []).append(annotation) 344*d4726bddSHONG Yifan for repo in repositories: 345*d4726bddSHONG Yifan _get_or_insert( 346*d4726bddSHONG Yifan _get_or_insert(repo_specific_annotations, repo, {}), 347*d4726bddSHONG Yifan crate, 348*d4726bddSHONG Yifan [], 349*d4726bddSHONG Yifan ).append(annotation) 350*d4726bddSHONG Yifan 351*d4726bddSHONG Yifan local_repos = [] 352*d4726bddSHONG Yifan 353*d4726bddSHONG Yifan for cfg in mod.tags.from_cargo + mod.tags.from_specs: 354*d4726bddSHONG Yifan if cfg.name in local_repos: 355*d4726bddSHONG Yifan fail("Defined two crate universes with the same name in the same MODULE.bazel file. Use the name tag to give them different names.") 356*d4726bddSHONG Yifan elif cfg.name in all_repos: 357*d4726bddSHONG Yifan fail("Defined two crate universes with the same name in different MODULE.bazel files. Either give one a different name, or use use_extension(isolate=True)") 358*d4726bddSHONG Yifan all_repos.append(cfg.name) 359*d4726bddSHONG Yifan local_repos.append(cfg.name) 360*d4726bddSHONG Yifan 361*d4726bddSHONG Yifan for cfg in mod.tags.from_cargo: 362*d4726bddSHONG Yifan annotations = _annotations_for_repo( 363*d4726bddSHONG Yifan module_annotations, 364*d4726bddSHONG Yifan repo_specific_annotations.get(cfg.name), 365*d4726bddSHONG Yifan ) 366*d4726bddSHONG Yifan 367*d4726bddSHONG Yifan cargo_lockfile = module_ctx.path(cfg.cargo_lockfile) 368*d4726bddSHONG Yifan manifests = {str(module_ctx.path(m)): str(m) for m in cfg.manifests} 369*d4726bddSHONG Yifan _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, cargo_lockfile = cargo_lockfile, manifests = manifests) 370*d4726bddSHONG Yifan 371*d4726bddSHONG Yifan for cfg in mod.tags.from_specs: 372*d4726bddSHONG Yifan # We don't have a Cargo.lock so the resolution can change. 373*d4726bddSHONG Yifan # We could maybe make this reproducible by using `-minimal-version` during resolution. 374*d4726bddSHONG Yifan # See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#minimal-versions 375*d4726bddSHONG Yifan reproducible = False 376*d4726bddSHONG Yifan 377*d4726bddSHONG Yifan annotations = _annotations_for_repo( 378*d4726bddSHONG Yifan module_annotations, 379*d4726bddSHONG Yifan repo_specific_annotations.get(cfg.name), 380*d4726bddSHONG Yifan ) 381*d4726bddSHONG Yifan 382*d4726bddSHONG Yifan packages = {p.package: _package_to_json(p) for p in mod.tags.spec} 383*d4726bddSHONG Yifan _generate_hub_and_spokes(module_ctx, cargo_bazel, cfg, annotations, packages = packages) 384*d4726bddSHONG Yifan 385*d4726bddSHONG Yifan for repo in repo_specific_annotations: 386*d4726bddSHONG Yifan if repo not in local_repos: 387*d4726bddSHONG Yifan fail("Annotation specified for repo %s, but the module defined repositories %s" % (repo, local_repos)) 388*d4726bddSHONG Yifan 389*d4726bddSHONG Yifan metadata_kwargs = {} 390*d4726bddSHONG Yifan if bazel_features.external_deps.extension_metadata_has_reproducible: 391*d4726bddSHONG Yifan metadata_kwargs["reproducible"] = reproducible 392*d4726bddSHONG Yifan 393*d4726bddSHONG Yifan return module_ctx.extension_metadata(**metadata_kwargs) 394*d4726bddSHONG Yifan 395*d4726bddSHONG Yifan_from_cargo = tag_class( 396*d4726bddSHONG Yifan doc = "Generates a repo @crates from a Cargo.toml / Cargo.lock pair", 397*d4726bddSHONG Yifan attrs = dict( 398*d4726bddSHONG Yifan name = attr.string( 399*d4726bddSHONG Yifan doc = "The name of the repo to generate", 400*d4726bddSHONG Yifan default = "crates", 401*d4726bddSHONG Yifan ), 402*d4726bddSHONG Yifan cargo_lockfile = CRATES_VENDOR_ATTRS["cargo_lockfile"], 403*d4726bddSHONG Yifan manifests = CRATES_VENDOR_ATTRS["manifests"], 404*d4726bddSHONG Yifan cargo_config = CRATES_VENDOR_ATTRS["cargo_config"], 405*d4726bddSHONG Yifan generate_binaries = CRATES_VENDOR_ATTRS["generate_binaries"], 406*d4726bddSHONG Yifan generate_build_scripts = CRATES_VENDOR_ATTRS["generate_build_scripts"], 407*d4726bddSHONG Yifan supported_platform_triples = CRATES_VENDOR_ATTRS["supported_platform_triples"], 408*d4726bddSHONG Yifan ), 409*d4726bddSHONG Yifan) 410*d4726bddSHONG Yifan 411*d4726bddSHONG Yifan# This should be kept in sync with crate_universe/private/crate.bzl. 412*d4726bddSHONG Yifan_annotation = tag_class( 413*d4726bddSHONG Yifan attrs = dict( 414*d4726bddSHONG Yifan repositories = attr.string_list( 415*d4726bddSHONG Yifan doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this annotation is applied to. Defaults to all repositories.", 416*d4726bddSHONG Yifan default = [], 417*d4726bddSHONG Yifan ), 418*d4726bddSHONG Yifan crate = attr.string( 419*d4726bddSHONG Yifan doc = "The name of the crate the annotation is applied to", 420*d4726bddSHONG Yifan mandatory = True, 421*d4726bddSHONG Yifan ), 422*d4726bddSHONG Yifan version = attr.string( 423*d4726bddSHONG Yifan doc = "The versions of the crate the annotation is applied to. Defaults to all versions.", 424*d4726bddSHONG Yifan default = "*", 425*d4726bddSHONG Yifan ), 426*d4726bddSHONG Yifan additive_build_file_content = attr.string( 427*d4726bddSHONG Yifan doc = "Extra contents to write to the bottom of generated BUILD files.", 428*d4726bddSHONG Yifan ), 429*d4726bddSHONG Yifan additive_build_file = attr.label( 430*d4726bddSHONG Yifan doc = "A file containing extra contents to write to the bottom of generated BUILD files.", 431*d4726bddSHONG Yifan ), 432*d4726bddSHONG Yifan alias_rule = attr.string( 433*d4726bddSHONG Yifan doc = "Alias rule to use instead of `native.alias()`. Overrides [render_config](#render_config)'s 'default_alias_rule'.", 434*d4726bddSHONG Yifan ), 435*d4726bddSHONG Yifan build_script_data = _relative_label_list( 436*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `cargo_build_script::data` attribute.", 437*d4726bddSHONG Yifan ), 438*d4726bddSHONG Yifan build_script_tools = _relative_label_list( 439*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `cargo_build_script::tools` attribute.", 440*d4726bddSHONG Yifan ), 441*d4726bddSHONG Yifan build_script_data_glob = attr.string_list( 442*d4726bddSHONG Yifan doc = "A list of glob patterns to add to a crate's `cargo_build_script::data` attribute", 443*d4726bddSHONG Yifan ), 444*d4726bddSHONG Yifan build_script_deps = _relative_label_list( 445*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `cargo_build_script::deps` attribute.", 446*d4726bddSHONG Yifan ), 447*d4726bddSHONG Yifan build_script_env = attr.string_dict( 448*d4726bddSHONG Yifan doc = "Additional environment variables to set on a crate's `cargo_build_script::env` attribute.", 449*d4726bddSHONG Yifan ), 450*d4726bddSHONG Yifan build_script_proc_macro_deps = _relative_label_list( 451*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `cargo_build_script::proc_macro_deps` attribute.", 452*d4726bddSHONG Yifan ), 453*d4726bddSHONG Yifan build_script_rundir = attr.string( 454*d4726bddSHONG Yifan doc = "An override for the build script's rundir attribute.", 455*d4726bddSHONG Yifan ), 456*d4726bddSHONG Yifan build_script_rustc_env = attr.string_dict( 457*d4726bddSHONG Yifan doc = "Additional environment variables to set on a crate's `cargo_build_script::env` attribute.", 458*d4726bddSHONG Yifan ), 459*d4726bddSHONG Yifan build_script_toolchains = attr.label_list( 460*d4726bddSHONG Yifan doc = "A list of labels to set on a crates's `cargo_build_script::toolchains` attribute.", 461*d4726bddSHONG Yifan ), 462*d4726bddSHONG Yifan compile_data = _relative_label_list( 463*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `rust_library::compile_data` attribute.", 464*d4726bddSHONG Yifan ), 465*d4726bddSHONG Yifan compile_data_glob = attr.string_list( 466*d4726bddSHONG Yifan doc = "A list of glob patterns to add to a crate's `rust_library::compile_data` attribute.", 467*d4726bddSHONG Yifan ), 468*d4726bddSHONG Yifan crate_features = attr.string_list( 469*d4726bddSHONG Yifan doc = "A list of strings to add to a crate's `rust_library::crate_features` attribute.", 470*d4726bddSHONG Yifan ), 471*d4726bddSHONG Yifan data = _relative_label_list( 472*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `rust_library::data` attribute.", 473*d4726bddSHONG Yifan ), 474*d4726bddSHONG Yifan data_glob = attr.string_list( 475*d4726bddSHONG Yifan doc = "A list of glob patterns to add to a crate's `rust_library::data` attribute.", 476*d4726bddSHONG Yifan ), 477*d4726bddSHONG Yifan deps = _relative_label_list( 478*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `rust_library::deps` attribute.", 479*d4726bddSHONG Yifan ), 480*d4726bddSHONG Yifan extra_aliased_targets = attr.string_dict( 481*d4726bddSHONG Yifan doc = "A list of targets to add to the generated aliases in the root crate_universe repository.", 482*d4726bddSHONG Yifan ), 483*d4726bddSHONG Yifan gen_binaries = attr.string_list( 484*d4726bddSHONG Yifan doc = "As a list, the subset of the crate's bins that should get `rust_binary` targets produced.", 485*d4726bddSHONG Yifan ), 486*d4726bddSHONG Yifan gen_all_binaries = attr.bool( 487*d4726bddSHONG Yifan doc = "If true, generates `rust_binary` targets for all of the crates bins", 488*d4726bddSHONG Yifan ), 489*d4726bddSHONG Yifan disable_pipelining = attr.bool( 490*d4726bddSHONG Yifan doc = "If True, disables pipelining for library targets for this crate.", 491*d4726bddSHONG Yifan ), 492*d4726bddSHONG Yifan gen_build_script = attr.string( 493*d4726bddSHONG Yifan doc = "An authorative flag to determine whether or not to produce `cargo_build_script` targets for the current crate. Supported values are 'on', 'off', and 'auto'.", 494*d4726bddSHONG Yifan values = _OPT_BOOL_VALUES.keys(), 495*d4726bddSHONG Yifan default = "auto", 496*d4726bddSHONG Yifan ), 497*d4726bddSHONG Yifan patch_args = attr.string_list( 498*d4726bddSHONG Yifan doc = "The `patch_args` attribute of a Bazel repository rule. See [http_archive.patch_args](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_args)", 499*d4726bddSHONG Yifan ), 500*d4726bddSHONG Yifan patch_tool = attr.string( 501*d4726bddSHONG Yifan doc = "The `patch_tool` attribute of a Bazel repository rule. See [http_archive.patch_tool](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_tool)", 502*d4726bddSHONG Yifan ), 503*d4726bddSHONG Yifan patches = attr.label_list( 504*d4726bddSHONG Yifan doc = "The `patches` attribute of a Bazel repository rule. See [http_archive.patches](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patches)", 505*d4726bddSHONG Yifan ), 506*d4726bddSHONG Yifan proc_macro_deps = _relative_label_list( 507*d4726bddSHONG Yifan doc = "A list of labels to add to a crate's `rust_library::proc_macro_deps` attribute.", 508*d4726bddSHONG Yifan ), 509*d4726bddSHONG Yifan rustc_env = attr.string_dict( 510*d4726bddSHONG Yifan doc = "Additional variables to set on a crate's `rust_library::rustc_env` attribute.", 511*d4726bddSHONG Yifan ), 512*d4726bddSHONG Yifan rustc_env_files = _relative_label_list( 513*d4726bddSHONG Yifan doc = "A list of labels to set on a crate's `rust_library::rustc_env_files` attribute.", 514*d4726bddSHONG Yifan ), 515*d4726bddSHONG Yifan rustc_flags = attr.string_list( 516*d4726bddSHONG Yifan doc = "A list of strings to set on a crate's `rust_library::rustc_flags` attribute.", 517*d4726bddSHONG Yifan ), 518*d4726bddSHONG Yifan shallow_since = attr.string( 519*d4726bddSHONG Yifan doc = "An optional timestamp used for crates originating from a git repository instead of a crate registry. This flag optimizes fetching the source code.", 520*d4726bddSHONG Yifan ), 521*d4726bddSHONG Yifan override_target_lib = attr.label( 522*d4726bddSHONG Yifan doc = "An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.", 523*d4726bddSHONG Yifan ), 524*d4726bddSHONG Yifan override_target_proc_macro = attr.label( 525*d4726bddSHONG Yifan doc = "An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.", 526*d4726bddSHONG Yifan ), 527*d4726bddSHONG Yifan override_target_build_script = attr.label( 528*d4726bddSHONG Yifan doc = "An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.", 529*d4726bddSHONG Yifan ), 530*d4726bddSHONG Yifan override_target_bin = attr.label( 531*d4726bddSHONG Yifan doc = "An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.", 532*d4726bddSHONG Yifan ), 533*d4726bddSHONG Yifan ), 534*d4726bddSHONG Yifan) 535*d4726bddSHONG Yifan 536*d4726bddSHONG Yifan_from_specs = tag_class( 537*d4726bddSHONG Yifan doc = "Generates a repo @crates from the defined `spec` tags", 538*d4726bddSHONG Yifan attrs = dict( 539*d4726bddSHONG Yifan name = attr.string(doc = "The name of the repo to generate", default = "crates"), 540*d4726bddSHONG Yifan cargo_config = CRATES_VENDOR_ATTRS["cargo_config"], 541*d4726bddSHONG Yifan generate_binaries = CRATES_VENDOR_ATTRS["generate_binaries"], 542*d4726bddSHONG Yifan generate_build_scripts = CRATES_VENDOR_ATTRS["generate_build_scripts"], 543*d4726bddSHONG Yifan supported_platform_triples = CRATES_VENDOR_ATTRS["supported_platform_triples"], 544*d4726bddSHONG Yifan ), 545*d4726bddSHONG Yifan) 546*d4726bddSHONG Yifan 547*d4726bddSHONG Yifan# This should be kept in sync with crate_universe/private/crate.bzl. 548*d4726bddSHONG Yifan_spec = tag_class( 549*d4726bddSHONG Yifan attrs = dict( 550*d4726bddSHONG Yifan package = attr.string( 551*d4726bddSHONG Yifan doc = "The explicit name of the package.", 552*d4726bddSHONG Yifan mandatory = True, 553*d4726bddSHONG Yifan ), 554*d4726bddSHONG Yifan version = attr.string( 555*d4726bddSHONG Yifan doc = "The exact version of the crate. Cannot be used with `git`.", 556*d4726bddSHONG Yifan ), 557*d4726bddSHONG Yifan artifact = attr.string( 558*d4726bddSHONG Yifan doc = "Set to 'bin' to pull in a binary crate as an artifact dependency. Requires a nightly Cargo.", 559*d4726bddSHONG Yifan ), 560*d4726bddSHONG Yifan lib = attr.bool( 561*d4726bddSHONG Yifan doc = "If using `artifact = 'bin'`, additionally setting `lib = True` declares a dependency on both the package's library and binary, as opposed to just the binary.", 562*d4726bddSHONG Yifan ), 563*d4726bddSHONG Yifan default_features = attr.bool( 564*d4726bddSHONG Yifan doc = "Maps to the `default-features` flag.", 565*d4726bddSHONG Yifan default = True, 566*d4726bddSHONG Yifan ), 567*d4726bddSHONG Yifan features = attr.string_list( 568*d4726bddSHONG Yifan doc = "A list of features to use for the crate.", 569*d4726bddSHONG Yifan ), 570*d4726bddSHONG Yifan git = attr.string( 571*d4726bddSHONG Yifan doc = "The Git url to use for the crate. Cannot be used with `version`.", 572*d4726bddSHONG Yifan ), 573*d4726bddSHONG Yifan branch = attr.string( 574*d4726bddSHONG Yifan doc = "The git branch of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds.", 575*d4726bddSHONG Yifan ), 576*d4726bddSHONG Yifan tag = attr.string( 577*d4726bddSHONG Yifan doc = "The git tag of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds.", 578*d4726bddSHONG Yifan ), 579*d4726bddSHONG Yifan rev = attr.string( 580*d4726bddSHONG Yifan doc = "The git revision of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified.", 581*d4726bddSHONG Yifan ), 582*d4726bddSHONG Yifan ), 583*d4726bddSHONG Yifan) 584*d4726bddSHONG Yifan 585*d4726bddSHONG Yifancrate = module_extension( 586*d4726bddSHONG Yifan implementation = _crate_impl, 587*d4726bddSHONG Yifan tag_classes = dict( 588*d4726bddSHONG Yifan from_cargo = _from_cargo, 589*d4726bddSHONG Yifan annotation = _annotation, 590*d4726bddSHONG Yifan from_specs = _from_specs, 591*d4726bddSHONG Yifan spec = _spec, 592*d4726bddSHONG Yifan ), 593*d4726bddSHONG Yifan) 594