Skip to content

Commit c9094a7

Browse files
authored
Rejigger git repo support (#113)
The existing way git crates are handles is a bit hacky - we do a deduped git clone for the external repo and then a per-crate `git worktree add` and symlink things around. We can instead do a single clone and add per-crate BUILD file overlaid onto it (which are still generated by per-crate helper repos). This avoids all usage of worktrees and symlinks, which incidentally should improve both Windows and R2C2 support.
1 parent 201ceb7 commit c9094a7

9 files changed

Lines changed: 446 additions & 288 deletions

rs/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ bzl_library(
3838
"//rs/private:annotations",
3939
"//rs/private:cargo_credentials",
4040
"//rs/private:cfg_parser",
41-
"//rs/private:crate_git_repository",
4241
"//rs/private:crate_repository",
4342
"//rs/private:downloader",
44-
"//rs/private:git_repository",
43+
"//rs/private:git_cargo_workspace_repository",
44+
"//rs/private:git_crate_metadata_repository",
4545
"//rs/private:lint_flags",
4646
"//rs/private:registry_config_repository",
4747
"//rs/private:registry_utils",

rs/extensions.bzl

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ load("@rs_rust_host_tools//:defs.bzl", "RS_HOST_CARGO_LABEL")
55
load("//rs/private:annotations.bzl", "annotation_for", "build_annotation_map", "well_known_annotation_snippet_paths")
66
load("//rs/private:cargo_credentials.bzl", "load_cargo_credentials")
77
load("//rs/private:cfg_parser.bzl", "cfg_matches_expr_for_cfg_attrs", "triple_to_cfg_attrs")
8-
load("//rs/private:crate_git_repository.bzl", "crate_git_repository")
98
load("//rs/private:crate_repository.bzl", "crate_repository", "local_crate_repository")
109
load("//rs/private:downloader.bzl", "download_metadata_for_git_crates", "new_downloader_state", "parse_git_url", "start_crate_registry_downloads", "start_github_downloads")
11-
load("//rs/private:git_repository.bzl", "git_repository")
10+
load("//rs/private:git_cargo_workspace_repository.bzl", "git_cargo_workspace_repository")
11+
load("//rs/private:git_crate_metadata_repository.bzl", "git_crate_metadata_repository")
1212
load("//rs/private:lint_flags.bzl", "cargo_toml_lint_flags")
1313
load("//rs/private:registry_config_repository.bzl", "registry_config_repository")
1414
load("//rs/private:registry_utils.bzl", "CRATES_IO_REGISTRY", "registry_config_repo_name")
@@ -24,8 +24,11 @@ def _spoke_repo(hub_name, name, version):
2424
s = s.replace("+", "-")
2525
return s
2626

27-
def _external_repo_for_git_source(remote, commit):
28-
return remote.replace("/", "_").replace(":", "_").replace("@", "_") + "_" + commit
27+
def _external_repo_for_git_source(hub_name, remote, commit):
28+
return hub_name + "__" + remote.replace("/", "_").replace(":", "_").replace("@", "_") + "_" + commit
29+
30+
def _git_crate_purl(name, version, remote, commit):
31+
return "pkg:cargo/%s@%s?vcs_url=git+%s@%s" % (name, version, remote, commit)
2932

3033
def _platform(triple, use_legacy_rules_rust_platforms):
3134
if use_legacy_rules_rust_platforms:
@@ -149,6 +152,28 @@ def _manifest_package_dir(manifest_path, repo_root):
149152

150153
return package_dir.removesuffix("/Cargo.toml")
151154

155+
def _git_crate_package_path(annotation, strip_prefix):
156+
workspace_dir = annotation.workspace_cargo_toml.removesuffix("Cargo.toml").removesuffix("/")
157+
crate_dir = (strip_prefix or "").removeprefix("./").removesuffix("/")
158+
159+
if workspace_dir and crate_dir:
160+
return _normalize_path(paths.normalize(paths.join(workspace_dir, crate_dir)))
161+
if workspace_dir:
162+
return _normalize_path(workspace_dir)
163+
return _normalize_path(crate_dir)
164+
165+
def _target_label(repo_name, package_path, target):
166+
if package_path:
167+
return "@%s//%s:%s" % (repo_name, package_path, target)
168+
return "@%s//:%s" % (repo_name, target)
169+
170+
def _additive_build_file_content(mctx, annotation):
171+
content = ""
172+
if annotation.additive_build_file:
173+
content += mctx.read(annotation.additive_build_file)
174+
content += annotation.additive_build_file_content
175+
return content
176+
152177
def _spec_to_dep_dict_inner(dep, spec, is_build = False):
153178
if type(spec) == "string":
154179
dep = {"name": dep}
@@ -746,8 +771,6 @@ crate.annotation(
746771

747772
kwargs = dict(
748773
hub_name = hub_name,
749-
additive_build_file = annotation.additive_build_file,
750-
additive_build_file_content = annotation.additive_build_file_content,
751774
gen_build_script = annotation.gen_build_script,
752775
build_script_deps = [],
753776
build_script_deps_select = _select(feature_resolutions.build_deps),
@@ -766,16 +789,14 @@ crate.annotation(
766789
crate_tags = annotation.tags,
767790
deps_select = _select(feature_resolutions.deps),
768791
aliases = feature_resolutions.aliases,
769-
gen_binaries = annotation.gen_binaries,
770792
crate_features = annotation.crate_features,
771793
crate_features_select = _select(feature_resolutions.features_enabled),
772-
patch_args = annotation.patch_args,
773-
patch_tool = annotation.patch_tool,
774-
patches = annotation.patches,
775794
use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms,
776795
)
777796

778797
repo_name = _spoke_repo(hub_name, crate_name, version)
798+
package["target_repo_name"] = repo_name
799+
package["target_package_path"] = ""
779800

780801
if source.startswith("sparse+"):
781802
checksum = package["checksum"]
@@ -789,11 +810,17 @@ crate.annotation(
789810

790811
crate_repository(
791812
name = repo_name,
813+
additive_build_file = annotation.additive_build_file,
814+
additive_build_file_content = annotation.additive_build_file_content,
792815
crate_name = crate_name,
793816
version = version,
794817
registry_config = "@%s//:dl" % registry_config_repo_name(hub_name, source),
795818
sbom_extra_qualifiers = qualifiers,
796819
checksum = checksum,
820+
gen_binaries = annotation.gen_binaries,
821+
patch_args = annotation.patch_args,
822+
patch_tool = annotation.patch_tool,
823+
patches = annotation.patches,
797824
# The repository will need to recompute these, but this lets us avoid serializing them.
798825
use_home_cargo_credentials = use_home_cargo_credentials,
799826
cargo_config = cargo_config,
@@ -807,26 +834,30 @@ crate.annotation(
807834
# TODO What PURL should that be ?
808835
local_crate_repository(
809836
name = repo_name,
837+
additive_build_file = annotation.additive_build_file,
838+
additive_build_file_content = annotation.additive_build_file_content,
839+
gen_binaries = annotation.gen_binaries,
840+
patch_args = annotation.patch_args,
841+
patch_tool = annotation.patch_tool,
842+
patches = annotation.patches,
810843
path = package["local_path"],
811844
**kwargs
812845
)
813846
elif source.startswith("git+"):
814847
remote, commit = parse_git_url(source)
815848

816-
strip_prefix = package.get("strip_prefix")
817-
workspace_cargo_toml = annotation.workspace_cargo_toml
818-
if workspace_cargo_toml != "Cargo.toml":
819-
strip_prefix = workspace_cargo_toml.removesuffix("Cargo.toml") + (strip_prefix or "")
849+
package_path = _git_crate_package_path(annotation, package.get("strip_prefix"))
850+
package["target_repo_name"] = _external_repo_for_git_source(hub_name, remote, commit)
851+
package["target_package_path"] = package_path
820852

821853
if dry_run:
822854
continue
823855

824-
crate_git_repository(
856+
git_crate_metadata_repository(
825857
name = repo_name,
826-
strip_prefix = strip_prefix,
827-
git_repo_label = "@" + _external_repo_for_git_source(remote, commit),
828-
remote = source,
829-
workspace_cargo_toml = annotation.workspace_cargo_toml,
858+
package_name = crate_name,
859+
package_version = version,
860+
purl = _git_crate_purl(crate_name, version, remote, commit),
830861
**kwargs
831862
)
832863
else:
@@ -836,35 +867,41 @@ crate.annotation(
836867

837868
mctx.report_progress("Initializing hub")
838869

870+
package_by_fq = {
871+
_fq_crate(package["name"], package["version"]): package
872+
for package in packages
873+
}
874+
839875
hub_contents = []
840876
for name, versions in versions_by_name.items():
841877
for version in versions:
842878
annotation = annotation_for(annotations, name, version)
843-
spoke_repo = _spoke_repo(hub_name, name, version)
879+
package = package_by_fq[_fq_crate(name, version)]
880+
target_repo_name = package["target_repo_name"]
881+
target_package_path = package["target_package_path"]
844882

845883
hub_contents.append("""
846884
alias(
847885
name = "{name}-{version}",
848-
actual = "@{spoke_repo}//:{name}",
849-
)""".format(name = name, version = version, spoke_repo = spoke_repo))
886+
actual = "{actual}",
887+
)""".format(name = name, version = version, actual = _target_label(target_repo_name, target_package_path, name)))
850888

851889
for binary in annotation.gen_binaries:
852890
hub_contents.append("""
853891
alias(
854892
name = "{name}-{version}__{binary}",
855-
actual = "@{spoke_repo}//:{binary}__bin",
856-
)""".format(name = name, version = version, binary = binary, spoke_repo = spoke_repo))
893+
actual = "{actual}",
894+
)""".format(name = name, version = version, binary = binary, actual = _target_label(target_repo_name, target_package_path, binary + "__bin")))
857895

858896
for alias_name, target in sorted(annotation.extra_aliased_targets.items()):
859897
hub_contents.append("""
860898
alias(
861899
name = "{alias_name}-{version}",
862-
actual = "@{spoke_repo}//:{target}",
900+
actual = "{actual}",
863901
)""".format(
864902
alias_name = alias_name,
865903
version = version,
866-
target = target,
867-
spoke_repo = spoke_repo,
904+
actual = _target_label(target_repo_name, target_package_path, target),
868905
))
869906

870907
workspace_versions = workspace_dep_versions_by_name.get(name)
@@ -1277,22 +1314,70 @@ def _crate_impl(mctx):
12771314

12781315
facts |= _generate_hub_and_spokes(mctx, cfg.name, annotations, suggested_annotation_snippet_paths, cargo_path, cfg.cargo_lock, cargo_toml_by_hub_name[cfg.name], hub_packages, cfg.platform_triples, cargo_credentials, cfg.cargo_config, cfg.validate_lockfile, cfg.debug, cfg.use_legacy_rules_rust_platforms)
12791316

1280-
# Lay down the git repos we will need; per-crate git_repository can clone from these.
1281-
git_sources = set()
1317+
# Lay down the git repos with generated per-crate BUILD overlays.
1318+
git_repos = {}
12821319
for mod in mctx.modules:
12831320
for cfg in mod.tags.from_cargo:
1321+
annotations = build_annotation_map(mod, cfg.name)
12841322
for package in packages_by_hub_name[cfg.name]:
12851323
source = package.get("source", "")
1286-
if source.startswith("git+"):
1287-
git_sources.add(source)
1288-
1289-
for git_source in git_sources:
1290-
remote, commit = parse_git_url(git_source)
1324+
if not source.startswith("git+"):
1325+
continue
12911326

1292-
git_repository(
1293-
name = _external_repo_for_git_source(remote, commit),
1294-
commit = commit,
1295-
remote = remote,
1327+
remote, commit = parse_git_url(source)
1328+
annotation = annotation_for(annotations, package["name"], package["version"])
1329+
repo_name = _external_repo_for_git_source(cfg.name, remote, commit)
1330+
git_repo = git_repos.get(repo_name)
1331+
if not git_repo:
1332+
git_repo = {
1333+
"build_files": {},
1334+
"gen_binaries": {},
1335+
"commit": commit,
1336+
"hub_name": cfg.name,
1337+
"patch_args": [],
1338+
"patch_tool": "",
1339+
"patches": {},
1340+
"remote": remote,
1341+
"workspace_cargo_toml": annotation.workspace_cargo_toml,
1342+
}
1343+
git_repos[repo_name] = git_repo
1344+
1345+
strip_prefix = package.get("strip_prefix")
1346+
if strip_prefix == None:
1347+
strip_prefix = json.decode(facts[source + "_" + package["name"]])["strip_prefix"]
1348+
package_path = _git_crate_package_path(annotation, strip_prefix)
1349+
build_file_path = paths.join(package_path, "BUILD.bazel") if package_path else "BUILD.bazel"
1350+
git_repo["build_files"][build_file_path] = _additive_build_file_content(mctx, annotation)
1351+
if annotation.gen_binaries:
1352+
git_repo["gen_binaries"][build_file_path] = annotation.gen_binaries
1353+
1354+
if annotation.patches:
1355+
patch_args = annotation.patch_args
1356+
patch_tool = annotation.patch_tool or ""
1357+
if git_repo["patches"] and (git_repo["patch_args"] != patch_args or git_repo["patch_tool"] != patch_tool):
1358+
fail("Git crates from %s use incompatible patch settings" % source)
1359+
1360+
git_repo["patch_args"] = patch_args
1361+
git_repo["patch_tool"] = patch_tool
1362+
for patch_file in annotation.patches:
1363+
git_repo["patches"][str(patch_file)] = patch_file
1364+
1365+
for repo_name, git_repo in git_repos.items():
1366+
kwargs = {}
1367+
if git_repo["gen_binaries"]:
1368+
kwargs["gen_binaries"] = git_repo["gen_binaries"]
1369+
1370+
git_cargo_workspace_repository(
1371+
name = repo_name,
1372+
build_files = git_repo["build_files"],
1373+
commit = git_repo["commit"],
1374+
hub_name = git_repo["hub_name"],
1375+
patch_args = git_repo["patch_args"],
1376+
patch_tool = git_repo["patch_tool"],
1377+
patches = git_repo["patches"].values(),
1378+
remote = git_repo["remote"],
1379+
workspace_cargo_toml = git_repo["workspace_cargo_toml"],
1380+
**kwargs
12961381
)
12971382

12981383
kwargs = dict(

rs/private/BUILD.bazel

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,11 @@ bzl_library(
3030
)
3131

3232
bzl_library(
33-
name = "crate_git_repository",
34-
srcs = ["crate_git_repository.bzl"],
33+
name = "git_crate_metadata_repository",
34+
srcs = ["git_crate_metadata_repository.bzl"],
3535
visibility = ["//rs:__subpackages__"],
3636
deps = [
3737
":repository_utils",
38-
":symlink_utils",
39-
":toml2json",
40-
"@bazel_tools//tools/build_defs/repo:git_worker.bzl",
41-
"@bazel_tools//tools/build_defs/repo:utils.bzl",
4238
],
4339
)
4440

@@ -51,6 +47,7 @@ bzl_library(
5147
":registry_utils",
5248
":repository_utils",
5349
":toml2json",
50+
"@bazel_features//:features",
5451
"@bazel_tools//tools/build_defs/repo:cache.bzl",
5552
"@bazel_tools//tools/build_defs/repo:utils.bzl",
5653
],
@@ -100,10 +97,15 @@ bzl_library(
10097
)
10198

10299
bzl_library(
103-
name = "git_repository",
104-
srcs = ["git_repository.bzl"],
100+
name = "git_cargo_workspace_repository",
101+
srcs = ["git_cargo_workspace_repository.bzl"],
105102
visibility = ["//rs:__subpackages__"],
106-
deps = ["@bazel_tools//tools/build_defs/repo:git_worker.bzl"],
103+
deps = [
104+
":repository_utils",
105+
":toml2json",
106+
"@bazel_tools//tools/build_defs/repo:git_worker.bzl",
107+
"@bazel_tools//tools/build_defs/repo:utils.bzl",
108+
],
107109
)
108110

109111
bzl_library(

0 commit comments

Comments
 (0)