1"""The http_jar repo rule, for downloading jars over HTTP.""" 2 3load("@bazel_tools//tools/build_defs/repo:cache.bzl", "CANONICAL_ID_DOC", "DEFAULT_CANONICAL_ID_ENV", "get_default_canonical_id") 4load("@bazel_tools//tools/build_defs/repo:utils.bzl", "get_auth", "update_attrs") 5 6_URL_DOC = """A URL to the jar that will be made available to Bazel. 7 8This must be a file, http or https URL. Redirections are followed. 9Authentication is not supported. 10 11More flexibility can be achieved by the urls parameter that allows 12to specify alternative URLs to fetch from.""" 13 14_URLS_DOC = """A list of URLs to the jar that will be made available to Bazel. 15 16Each entry must be a file, http or https URL. Redirections are followed. 17Authentication is not supported. 18 19URLs are tried in order until one succeeds, so you should list local mirrors first. 20If all downloads fail, the rule will fail.""" 21 22_AUTH_PATTERN_DOC = """An optional dict mapping host names to custom authorization patterns. 23 24If a URL's host name is present in this dict the value will be used as a pattern when 25generating the authorization header for the http request. This enables the use of custom 26authorization schemes used in a lot of common cloud storage providers. 27 28The pattern currently supports 2 tokens: <code><login></code> and 29<code><password></code>, which are replaced with their equivalent value 30in the netrc file for the same host name. After formatting, the result is set 31as the value for the <code>Authorization</code> field of the HTTP request. 32 33Example attribute and netrc for a http download to an oauth2 enabled API using a bearer token: 34 35<pre> 36auth_patterns = { 37 "storage.cloudprovider.com": "Bearer <password>" 38} 39</pre> 40 41netrc: 42 43<pre> 44machine storage.cloudprovider.com 45 password RANDOM-TOKEN 46</pre> 47 48The final HTTP request would have the following header: 49 50<pre> 51Authorization: Bearer RANDOM-TOKEN 52</pre> 53""" 54 55def _get_source_urls(ctx): 56 """Returns source urls provided via the url, urls attributes. 57 58 Also checks that at least one url is provided.""" 59 if not ctx.attr.url and not ctx.attr.urls: 60 fail("At least one of url and urls must be provided") 61 62 source_urls = [] 63 if ctx.attr.urls: 64 source_urls = ctx.attr.urls 65 if ctx.attr.url: 66 source_urls = [ctx.attr.url] + source_urls 67 return source_urls 68 69def _update_integrity_attr(ctx, attrs, download_info): 70 # We don't need to override the integrity attribute if sha256 is already specified. 71 integrity_override = {} if ctx.attr.sha256 else {"integrity": download_info.integrity} 72 return update_attrs(ctx.attr, attrs.keys(), integrity_override) 73 74_HTTP_JAR_BUILD = """\ 75load("{java_import_bzl}", "java_import") 76 77java_import( 78 name = 'jar', 79 jars = ["{file_name}"], 80 visibility = ['//visibility:public'], 81) 82 83filegroup( 84 name = 'file', 85 srcs = ["{file_name}"], 86 visibility = ['//visibility:public'], 87) 88 89""" 90 91def _http_jar_impl(ctx): 92 """Implementation of the http_jar rule.""" 93 source_urls = _get_source_urls(ctx) 94 downloaded_file_name = ctx.attr.downloaded_file_name 95 download_info = ctx.download( 96 source_urls, 97 "jar/" + downloaded_file_name, 98 ctx.attr.sha256, 99 canonical_id = ctx.attr.canonical_id or get_default_canonical_id(ctx, source_urls), 100 auth = get_auth(ctx, source_urls), 101 integrity = ctx.attr.integrity, 102 ) 103 ctx.file("jar/BUILD", _HTTP_JAR_BUILD.format( 104 java_import_bzl = str(Label("//java:java_import.bzl")), 105 file_name = downloaded_file_name, 106 )) 107 108 return _update_integrity_attr(ctx, _http_jar_attrs, download_info) 109 110_http_jar_attrs = { 111 "sha256": attr.string( 112 doc = """The expected SHA-256 of the jar downloaded. 113 114This must match the SHA-256 of the jar downloaded. _It is a security risk 115to omit the SHA-256 as remote files can change._ At best omitting this 116field will make your build non-hermetic. It is optional to make development 117easier but either this attribute or `integrity` should be set before shipping.""", 118 ), 119 "integrity": attr.string( 120 doc = """Expected checksum in Subresource Integrity format of the jar downloaded. 121 122This must match the checksum of the file downloaded. _It is a security risk 123to omit the checksum as remote files can change._ At best omitting this 124field will make your build non-hermetic. It is optional to make development 125easier but either this attribute or `sha256` should be set before shipping.""", 126 ), 127 "canonical_id": attr.string( 128 doc = CANONICAL_ID_DOC, 129 ), 130 "url": attr.string(doc = _URL_DOC + "\n\nThe URL must end in `.jar`."), 131 "urls": attr.string_list(doc = _URLS_DOC + "\n\nAll URLs must end in `.jar`."), 132 "netrc": attr.string( 133 doc = "Location of the .netrc file to use for authentication", 134 ), 135 "auth_patterns": attr.string_dict( 136 doc = _AUTH_PATTERN_DOC, 137 ), 138 "downloaded_file_name": attr.string( 139 default = "downloaded.jar", 140 doc = "Filename assigned to the jar downloaded", 141 ), 142} 143 144http_jar = repository_rule( 145 implementation = _http_jar_impl, 146 attrs = _http_jar_attrs, 147 environ = [DEFAULT_CANONICAL_ID_ENV], 148 doc = 149 """Downloads a jar from a URL and makes it available as java_import 150 151Downloaded files must have a .jar extension. 152 153Examples: 154 Suppose the current repository contains the source code for a chat program, rooted at the 155 directory `~/chat-app`. It needs to depend on an SSL library which is available from 156 `http://example.com/openssl-0.2.jar`. 157 158 Targets in the `~/chat-app` repository can depend on this target if the following lines are 159 added to `~/chat-app/MODULE.bazel`: 160 161 ```python 162 http_jar = use_repo_rule("@rules_java//java:http_jar.bzl", "http_jar") 163 164 http_jar( 165 name = "my_ssl", 166 url = "http://example.com/openssl-0.2.jar", 167 sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 168 ) 169 ``` 170 171 Targets would specify `@my_ssl//jar` as a dependency to depend on this jar. 172 173 You may also reference files on the current system (localhost) by using "file:///path/to/file" 174 if you are on Unix-based systems. If you're on Windows, use "file:///c:/path/to/file". In both 175 examples, note the three slashes (`/`) -- the first two slashes belong to `file://` and the third 176 one belongs to the absolute path to the file. 177""", 178) 179