| Lorenz Brun | 4c4a704 | 2025-10-29 15:37:32 +0100 | [diff] [blame] | 1 | load( |
| 2 | "@bazel_tools//tools/build_defs/repo:cache.bzl", |
| 3 | "CANONICAL_ID_DOC", |
| 4 | "DEFAULT_CANONICAL_ID_ENV", |
| 5 | "get_default_canonical_id", |
| 6 | ) |
| 7 | load( |
| 8 | "@bazel_tools//tools/build_defs/repo:utils.bzl", |
| 9 | "get_auth", |
| 10 | "update_attrs", |
| 11 | "workspace_and_buildfile", |
| 12 | ) |
| 13 | |
| 14 | _http_archive_deb_attrs = { |
| 15 | "url": attr.string(doc = "A URL to a deb file. See http_archive for more info."), |
| 16 | "urls": attr.string_list(doc = "A list of URLs to a deb file. See http_archive for more info."), |
| 17 | "integrity": attr.string( |
| 18 | doc = """Expected checksum in Subresource Integrity format of the file downloaded. |
| 19 | |
| 20 | This must match the checksum of the file downloaded. _It is a security risk |
| 21 | to omit the checksum as remote files can change._ At best omitting this |
| 22 | field will make your build non-hermetic. It is optional to make development |
| 23 | easier but this attribute should be set before shipping.""", |
| 24 | ), |
| 25 | "netrc": attr.string( |
| 26 | doc = "Location of the .netrc file to use for authentication", |
| 27 | ), |
| 28 | "auth_patterns": attr.string_dict( |
| 29 | doc = "See http_archive", |
| 30 | ), |
| 31 | "canonical_id": attr.string( |
| 32 | doc = CANONICAL_ID_DOC, |
| 33 | ), |
| 34 | "build_file": attr.label( |
| 35 | allow_single_file = True, |
| 36 | doc = |
| 37 | "The file to use as the BUILD file for this repository." + |
| 38 | "This attribute is an absolute label (use '@//' for the main " + |
| 39 | "repo). The file does not need to be named BUILD, but can " + |
| 40 | "be (something like BUILD.new-repo-name may work well for " + |
| 41 | "distinguishing it from the repository's actual BUILD files. " + |
| 42 | "Either build_file or build_file_content can be specified, but " + |
| 43 | "not both.", |
| 44 | ), |
| 45 | "build_file_content": attr.string( |
| 46 | doc = |
| 47 | "The content for the BUILD file for this repository. " + |
| 48 | "Either build_file or build_file_content can be specified, but " + |
| 49 | "not both.", |
| 50 | ), |
| 51 | "workspace_file": attr.label( |
| 52 | doc = "No-op attribute; do not use.", |
| 53 | ), |
| 54 | "workspace_file_content": attr.string( |
| 55 | doc = "No-op attribute; do not use.", |
| 56 | ), |
| 57 | } |
| 58 | |
| 59 | def _get_source_urls(ctx): |
| 60 | """Returns source urls provided via the url, urls attributes. |
| 61 | |
| 62 | Also checks that at least one url is provided.""" |
| 63 | if not ctx.attr.url and not ctx.attr.urls: |
| 64 | fail("At least one of url and urls must be provided") |
| 65 | |
| 66 | source_urls = [] |
| 67 | if ctx.attr.urls: |
| 68 | source_urls = ctx.attr.urls |
| 69 | if ctx.attr.url: |
| 70 | source_urls = [ctx.attr.url] + source_urls |
| 71 | return source_urls |
| 72 | |
| 73 | def _http_archive_deb_impl(ctx): |
| 74 | """Implementation of the http_archive_deb rule.""" |
| 75 | if ctx.attr.build_file and ctx.attr.build_file_content: |
| 76 | fail("Only one of build_file and build_file_content can be provided.") |
| 77 | |
| 78 | source_urls = _get_source_urls(ctx) |
| 79 | download_info = ctx.download_and_extract( |
| 80 | source_urls, |
| 81 | output = "debian-package", |
| 82 | type = ".deb", |
| 83 | canonical_id = ctx.attr.canonical_id or get_default_canonical_id(ctx, source_urls), |
| 84 | auth = get_auth(ctx, source_urls), |
| 85 | integrity = ctx.attr.integrity, |
| 86 | ) |
| 87 | files = ctx.path("debian-package").readdir() |
| 88 | |
| 89 | data_archive = None |
| 90 | control_archive = None |
| 91 | has_marker = False |
| 92 | for f in files: |
| 93 | if f.basename.startswith("data.tar."): |
| 94 | data_archive = f.basename |
| 95 | elif f.basename.startswith("control.tar."): |
| 96 | control_archive = f.basename |
| 97 | elif f.basename == "debian-binary": |
| 98 | has_marker = True |
| 99 | |
| 100 | if not has_marker: |
| 101 | fail("deb package does not contain a debian-binary marker file, check the file.") |
| 102 | if not data_archive: |
| 103 | fail("Failed to find data.tar.* archive in .deb contents.") |
| 104 | if not control_archive: |
| 105 | fail("Failed to find control.tar.* archive in .deb contents.") |
| 106 | |
| 107 | ctx.extract( |
| 108 | archive = "debian-package/" + data_archive, |
| 109 | ) |
| 110 | ctx.extract( |
| 111 | archive = "debian-package/" + control_archive, |
| 112 | output = "debian", |
| 113 | ) |
| 114 | ctx.delete("debian-package") |
| 115 | workspace_and_buildfile(ctx) |
| 116 | |
| 117 | if ctx.attr.integrity: |
| 118 | return ctx.repo_metadata(reproducible = True) |
| 119 | |
| 120 | return ctx.repo_metadata(attrs_for_reproducibility = update_attrs(ctx.attr, _http_archive_deb_attrs.keys(), {"integrity": download_info.integrity})) |
| 121 | |
| 122 | http_archive_deb = repository_rule( |
| 123 | implementation = _http_archive_deb_impl, |
| 124 | attrs = _http_archive_deb_attrs, |
| 125 | environ = [DEFAULT_CANONICAL_ID_ENV], |
| 126 | doc = """http_archive for Debian packages. Extracts all contents into a |
| 127 | repository, control files are extracted into a `debian` subfolder.""", |
| 128 | ) |