From 0da4130bb9c3a354e20042b730bea8ce7f279394 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 3 Apr 2024 06:42:44 +0200 Subject: [PATCH] Bazel: add LFS rules --- java/kotlin-extractor/BUILD.bazel | 0 misc/bazel/internal/BUILD.bazel | 0 misc/bazel/internal/git_lfs_smudge.py | 16 ++++++ misc/bazel/lfs.bzl | 73 +++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 java/kotlin-extractor/BUILD.bazel create mode 100644 misc/bazel/internal/BUILD.bazel create mode 100755 misc/bazel/internal/git_lfs_smudge.py create mode 100644 misc/bazel/lfs.bzl diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel new file mode 100644 index 00000000000..e69de29bb2d diff --git a/misc/bazel/internal/BUILD.bazel b/misc/bazel/internal/BUILD.bazel new file mode 100644 index 00000000000..e69de29bb2d diff --git a/misc/bazel/internal/git_lfs_smudge.py b/misc/bazel/internal/git_lfs_smudge.py new file mode 100755 index 00000000000..397975473c3 --- /dev/null +++ b/misc/bazel/internal/git_lfs_smudge.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import sys +import pathlib +import subprocess +import os + +sources = [pathlib.Path(arg).resolve() for arg in sys.argv[1:]] +source_dir = pathlib.Path(os.path.commonpath(src.parent for src in sources)) + +for src in sources: + with open(src, 'rb') as input: + lfs_pointer = subprocess.run(["git", "lfs", "clean", "--", src], + stdin=input, stdout=subprocess.PIPE, check=True, cwd=source_dir).stdout + with open(src.name, 'wb') as output: + subprocess.run(["git", "lfs", "smudge", "--", src], input=lfs_pointer, stdout=output, check=True, cwd=source_dir) diff --git a/misc/bazel/lfs.bzl b/misc/bazel/lfs.bzl new file mode 100644 index 00000000000..a5559d55a74 --- /dev/null +++ b/misc/bazel/lfs.bzl @@ -0,0 +1,73 @@ +def _smudge(repository_ctx, srcs): + for src in srcs: + repository_ctx.watch(src) + script = Label("//misc/bazel/internal:git_lfs_smudge.py") + python = repository_ctx.which("python3") or repository_ctx.which("python") + if not python: + fail("Neither python3 nor python executables found") + res = repository_ctx.execute([python, script] + srcs, quiet = False) + if res.return_code != 0: + fail("git LFS smudging failed while instantiating @%s" % repository_ctx.name) + +def _download_and_extract_lfs(repository_ctx): + attr = repository_ctx.attr + src = repository_ctx.path(attr.src) + if attr.build_file_content and attr.build_file: + fail("You should specify only one among build_file_content and build_file for rule @%s" % repository_ctx.name) + _smudge(repository_ctx, [src]) + repository_ctx.extract(src.basename, stripPrefix = attr.strip_prefix) + repository_ctx.delete(src.basename) + if attr.build_file_content: + repository_ctx.file("BUILD.bazel", attr.build_file_content) + elif attr.build_file: + repository_ctx.symlink(attr.build_file, "BUILD.bazel") + +def _download_lfs(repository_ctx): + attr = repository_ctx.attr + if int(bool(attr.srcs)) + int(bool(attr.dir)) != 1: + fail("Exactly one between `srcs` and `dir` must be defined for @%s" % repository_ctx.name) + if attr.srcs: + srcs = [repository_ctx.path(src) for src in attr.srcs] + else: + dir = repository_ctx.path(attr.dir) + if not dir.is_dir: + fail("`dir` not a directory in @%s" % repository_ctx.name) + srcs = [f for f in dir.readdir() if not f.is_dir] + _smudge(repository_ctx, srcs) + + # with bzlmod the name is qualified with `~` separators, and we want the base name here + name = repository_ctx.name.split("~")[-1] + repository_ctx.file("BUILD.bazel", """ +exports_files({files}) + +filegroup( + name = "{name}", + srcs = {files}, + visibility = ["//visibility:public"], +) +""".format(name = name, files = repr([src.basename for src in srcs]))) + +lfs_archive = repository_rule( + doc = "Export the contents from an on-demand LFS archive. The corresponding path should be added to be ignored " + + "in `.lfsconfig`.", + implementation = _download_and_extract_lfs, + attrs = { + "src": attr.label(mandatory = True, doc = "Local path to the LFS archive to extract."), + "build_file_content": attr.string(doc = "The content for the BUILD file for this repository. " + + "Either build_file or build_file_content can be specified, but not both."), + "build_file": attr.label(doc = "The file to use as the BUILD file for this repository. " + + "Either build_file or build_file_content can be specified, but not both."), + "strip_prefix": attr.string(default = "", doc = "A directory prefix to strip from the extracted files. "), + }, +) + +lfs_files = repository_rule( + doc = "Export LFS files for on-demand download. Exactly one between `srcs` and `dir` must be defined. The " + + "corresponding paths should be added to be ignored in `.lfsconfig`.", + implementation = _download_lfs, + attrs = { + "srcs": attr.label_list(doc = "Local paths to the LFS files to export."), + "dir": attr.label(doc = "Local path to a directory containing LFS files to export. Only the direct contents " + + "of the directory are exported"), + }, +)