-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathextensions.bzl
439 lines (366 loc) · 20.1 KB
/
extensions.bzl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
"""Repository rule for downloading the correct version of doxygen using module extensions."""
load("@bazel_tools//tools/build_defs/repo:cache.bzl", "get_default_canonical_id")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "get_auth")
def _get_current_platform(ctx):
"""
Get the current platform.
It uses the context to get the current platform and architecture and returns the platform name.
The string returned is one of the following:
- windows
- mac
- mac-arm
- linux
- linux-arm
Args:
ctx: a [repository context](https://bazel.build/rules/lib/builtins/repository_ctx) object
or a [module context](https://bazel.build/rules/lib/builtins/module_ctx) object
containing the repository's attributes
"""
if ctx.os.name.startswith("windows"):
return "windows"
if ctx.os.name.startswith("mac") and (ctx.os.arch == "amd64" or ctx.os.arch == "i686" or ctx.os.arch == "x86_64"):
return "mac"
if ctx.os.name.startswith("mac") and ctx.os.arch == "aarch64":
return "mac-arm"
if ctx.os.name == "linux" and (ctx.os.arch == "amd64" or ctx.os.arch == "i686" or ctx.os.arch == "x86_64"):
return "linux"
if ctx.os.name == "linux" and ctx.os.arch == "aarch64":
return "linux-arm"
fail("Unsupported platform: %s (%s)" % (ctx.os.name, ctx.os.arch))
def _doxygen_repository(ctx):
"""
Repository rule for doxygen.
Used to create a local repository for doxygen, containing the installed doxygen binary and all the necessary files to run the doxygen macro.
Args:
ctx: a [repository context](https://bazel.build/rules/lib/builtins/repository_ctx) object containing the repository's attributes
"""
# Copy the necessary files to the repository by reading them from the current repository
if len(ctx.attr.versions) != len(ctx.attr.sha256s) or len(ctx.attr.versions) != len(ctx.attr.platforms) or len(ctx.attr.versions) != len(ctx.attr.executables):
fail("The number of versions, sha256s, platforms and executables must be the same")
for platform in ctx.attr.platforms:
if platform not in ("windows", "mac", "mac-arm", "linux", "linux-arm"):
fail("Unsupported platform: '%s'. Available options are (windows, mac, mac-arm, linux, linux-arm)" % platform)
ctx.file("WORKSPACE", "workspace(name = %s)\n" % repr(ctx.name))
ctx.file("doxygen.bzl", ctx.read(ctx.attr.doxygen_bzl))
ctx.file("BUILD.bazel", ctx.read(ctx.attr.build))
ctx.file("Doxyfile.template", ctx.read(ctx.attr.doxyfile_template))
for doxygen_version, sha256, platform, executable in zip(ctx.attr.versions, ctx.attr.sha256s, ctx.attr.platforms, ctx.attr.executables):
if platform == "windows" and _get_current_platform(ctx) == platform:
executable_destination = "windows/doxygen.exe"
elif platform in ("mac", "mac-arm") and _get_current_platform(ctx) == platform:
executable_destination = "mac/doxygen"
elif platform in ("linux", "linux-arm") and _get_current_platform(ctx) == platform:
executable_destination = "linux/doxygen"
else:
continue # Skip the platform if it does not match the current platform. No operation will be performed
# If the version is set to 0.0.0 use the installed version of doxygen from the PATH
# No download will be performed
# This happens only if the platform matches the current platform
if doxygen_version == "0.0.0":
ctx.file(executable_destination, ctx.read(ctx.which("doxygen")), legacy_utf8 = False)
continue
# If an executable is provided, use it instead of downloading the doxygen binary
if executable != "":
ctx.file(executable_destination, ctx.read(Label(executable)), legacy_utf8 = False)
continue
url = "https://github.com/doxygen/doxygen/releases/download/Release_%s/doxygen-%s.%s"
doxygen_version_dash = doxygen_version.replace(".", "_")
download_output = "doxygen-dir"
if platform == "windows":
# For windows, download the zip file and extract the executable and dll
url = url % (doxygen_version_dash, doxygen_version, "windows.x64.bin.zip")
ctx.download_and_extract(
url = url,
output = download_output,
sha256 = sha256,
type = "zip",
canonical_id = get_default_canonical_id(ctx, [url]),
auth = get_auth(ctx, [url]),
)
# Copy the doxygen executable (and dll) to the repository
ctx.file(executable_destination, ctx.read("doxygen-dir/doxygen.exe"), legacy_utf8 = False)
ctx.file("windows/libclang.dll", ctx.read("doxygen-dir/libclang.dll"), legacy_utf8 = False)
elif platform in ("mac", "mac-arm"):
# For mac, download the dmg file, mount it and copy the executable
url = url % (doxygen_version_dash, doxygen_version, "dmg")
download_output = "doxygen.dmg"
ctx.download(
url = url,
output = download_output,
sha256 = sha256,
canonical_id = get_default_canonical_id(ctx, [url]),
auth = get_auth(ctx, [url]),
)
# Mount the dmg file
ctx.execute(["hdiutil", "attach", "-nobrowse", "-readonly", "-mountpoint", "doxygen-mount", download_output])
# Copy the doxygen executable to the repository
ctx.file(executable_destination, ctx.read("doxygen-mount/Doxygen.app/Contents/Resources/doxygen"), legacy_utf8 = False)
# Unmount the dmg file
ctx.execute(["hdiutil", "detach", "doxygen-mount"])
elif platform in ("linux", "linux-arm"):
# For linux, download the tar.gz file and extract the executable
url = url % (doxygen_version_dash, doxygen_version, "linux.bin.tar.gz")
ctx.download_and_extract(
url = url,
output = download_output,
sha256 = sha256,
type = "tar.gz",
canonical_id = get_default_canonical_id(ctx, [url]),
auth = get_auth(ctx, [url]),
stripPrefix = "doxygen-%s" % doxygen_version,
)
# Copy the doxygen executable to the repository
ctx.file(executable_destination, ctx.read("doxygen-dir/bin/doxygen"), legacy_utf8 = False)
# Delete temporary files
ctx.delete(download_output)
doxygen_repository = repository_rule(
implementation = _doxygen_repository,
doc = """
Repository rule for doxygen.
It can be provided with a configuration for each of the three platforms (windows, mac, mac-arm, linux, linux-arm) to download the correct version of doxygen only when the configuration matches the current platform.
Depending on the version, the behavior will change:
- If the version is set to `0.0.0`, the repository will use the installed version of doxygen, getting the binary from the PATH.
- If a version is specified, the repository will download the correct version of doxygen and make it available to the requesting module.
> [!Warning]
> If version is set to `0.0.0`, the rules needs doxygen to be installed on your system and the binary (named doxygen) must available in the PATH.
> Keep in mind that this will break the hermeticity of your build, as it will now depend on the environment.
You can further customize the repository by specifying the `doxygen_bzl`, `build`, and `doxyfile_template` attributes, but the default values should be enough for most use cases.
### Example
```starlark
# Download the os specific version 1.12.0 of doxygen supporting all the indicated platforms
doxygen_repository(
name = "doxygen",
versions = ["1.12.0", "1.12.0", "1.12.0"],
sha256s = [
"07f1c92cbbb32816689c725539c0951f92c6371d3d7f66dfa3192cbe88dd3138",
"6ace7dde967d41f4e293d034a67eb2c7edd61318491ee3131112173a77344001",
"3c42c3f3fb206732b503862d9c9c11978920a8214f223a3950bbf2520be5f647",
]
platforms = ["windows", "mac", "linux"],
executables = ["", "", ""],
)
# Use the system installed version of doxygen on linux and download version 1.11.0 for windows. Use the provided executable on mac-arm
doxygen_repository(
name = "doxygen",
version = ["0.0.0", "1.11.0", ""],
sha256s = ["", "478fc9897d00ca181835d248a4d3e5c83c26a32d1c7571f4321ddb0f2e97459f", ""],
platforms = ["linux", "windows", "mac-arm"],
executables = ["", "", "/path/to/doxygen"],
)
```
""",
attrs = {
"versions": attr.string_list(
doc = "List of versions of doxygen to use. If set to `0.0.0`, the doxygen executable will be assumed to be available from the PATH. Must be the same length as `sha256s`, `platforms` and `executables`.",
mandatory = True,
allow_empty = False,
),
"sha256s": attr.string_list(
doc = "List of sha256 hashes of the doxygen archive. Must be the same length as `versions, `platforms` and `executables`.",
mandatory = True,
allow_empty = False,
),
"platforms": attr.string_list(
doc = "List of platforms to download the doxygen binary for. Available options are (windows, mac, mac-arm, linux, linux-arm). Must be the same length as `version`, `sha256s` and `executables`.",
mandatory = True,
allow_empty = False,
),
"executables": attr.string_list(
doc = "List of paths to doxygen executables to use. If set, no download will take place and the provided doxygen executable will be used. Mutually exclusive with `version`. Must be the same length as `version`, `sha256s` and `platforms`.",
allow_empty = False,
mandatory = True,
),
"doxygen_bzl": attr.label(
doc = "The starlark file containing the doxygen macro",
allow_single_file = True,
default = Label("@rules_doxygen//doxygen:doxygen.bzl"),
),
"build": attr.label(
doc = "The BUILD file of the repository",
allow_single_file = True,
default = Label("@rules_doxygen//doxygen:BUILD.bazel"),
),
"doxyfile_template": attr.label(
doc = "The Doxyfile template to use",
allow_single_file = True,
default = Label("@rules_doxygen//doxygen:Doxyfile.template"),
),
},
)
def _doxygen_extension_impl(ctx):
"""_doxygen_module_impl
Downloads the correct version of doxygen and make the repository available to the requesting module.
Args:
ctx: a [module context](https://bazel.build/rules/lib/builtins/module_ctx) object containing the module's attributes
"""
for mod in ctx.modules:
name = ""
platforms = []
versions = []
sha256s = []
executables = []
for attr in mod.tags.repository:
if attr.name == "":
fail("The name of the repository cannot be empty")
if name != "":
fail("The name of the repository was already specified: '%s'. Cannot be overridden with '%s'" % (name, attr.name))
name = attr.name
default_configurations = {
"windows": struct(version = "1.13.2", sha256 = "dedbcf1d05911afc003989d33194ab95539e2ef4535cc00143b5917c22614406", executable = ""),
"mac": struct(version = "1.13.2", sha256 = "dc981a4212875e8c6b81727eb43bb634167df0d4bc1681fb9053cc850e918acf", executable = ""),
"mac-arm": struct(version = "1.13.2", sha256 = "dc981a4212875e8c6b81727eb43bb634167df0d4bc1681fb9053cc850e918acf", executable = ""),
"linux": struct(version = "1.13.2", sha256 = "f2c0a349403bc5b5ade3f501301e32b49ea31b3182666954bc398452fbc0dd1c", executable = ""),
"linux-arm": struct(version = "1.13.2", sha256 = "f2c0a349403bc5b5ade3f501301e32b49ea31b3182666954bc398452fbc0dd1c", executable = ""),
}
# Otherwise, add all the configurations (version and sha256) for each platform
for attr in mod.tags.configuration:
platform = attr.platform if attr.platform != "" else _get_current_platform(ctx)
if attr.version != "" and attr.executable != None:
fail("`Version` and `executable` are mutually exclusive")
if attr.version == "" and attr.executable == None:
fail("Exactly one between `version` and `executable` must be specified")
if platform not in default_configurations:
fail("Unsupported platform: '%s'. Available options are (windows, mac, mac-arm, linux, linux-arm)" % platform)
if platform in platforms:
fail("Doxygen configuration for platform '%s' was already specified: (version = '%s', sha256 = '%s', executable = %s)" % (platform, versions[platforms.index(platform)], sha256s[platforms.index(platform)], executables[platforms.index(platform)]))
platforms.append(platform)
versions.append(attr.version)
sha256s.append(attr.sha256 if attr.sha256 != "" else "0" * 64)
executables.append(str(attr.executable) if attr.executable != None else "")
# If no version is specified for a platform, use the default
for platform in default_configurations:
if platform not in platforms:
platforms.append(platform)
versions.append(default_configurations[platform].version)
sha256s.append(default_configurations[platform].sha256)
executables.append(default_configurations[platform].executable)
doxygen_repository(
name = name if name != "" else "doxygen",
versions = versions,
sha256s = sha256s,
platforms = platforms,
executables = executables,
)
_doxygen_configuration_tag = tag_class(attrs = {
"version": attr.string(doc = "The version of doxygen to use. If set to `0.0.0`, the doxygen executable will be assumed to be available from the PATH. Mutually exclusive with `executable`."),
"sha256": attr.string(doc = "The sha256 hash of the doxygen archive. If not specified, an all-zero hash will be used."),
"platform": attr.string(doc = "The target platform for the doxygen binary. Available options are (windows, mac, mac-arm, linux, linux-arm). If not specified, it will select the platform it is currently running on."),
"executable": attr.label(doc = "The doxygen executable to use. If set, no download will take place and the provided doxygen executable will be used. Mutually exclusive with `version`."),
})
_doxygen_repository_tag = tag_class(attrs = {
"name": attr.string(doc = "The name of the repository the extension will create. Useful if you don't use 'rules_doxygen' as a dev_dependency, since it will avoid name collision for module depending on yours. Must be the same for all configurations. Defaults to 'doxygen'.", mandatory = True),
})
doxygen_extension = module_extension(
implementation = _doxygen_extension_impl,
tag_classes = {"configuration": _doxygen_configuration_tag, "repository": _doxygen_repository_tag},
doc = """
Module extension for declaring the doxygen configurations to use.
The resulting repository will have the following targets:
- `@doxygen//:doxygen.bzl`, containing the doxygen macro used to generate the documentation.
- `@doxygen//:Doxyfile.template`, default Doxyfile template used to generate the Doxyfile.
The extension will create a default configuration for all platforms with the version `1.13.2` of Doxygen.
You can override this value with a custom one for each supported platform, i.e. _windows_, _mac_, _mac-arm_, _linux_ and _linux-arm_.
```bzl
# MODULE.bazel file
bazel_dep(name = "rules_doxygen", version = "...", dev_dependency = True)
doxygen_extension = use_extension("@rules_doxygen//:extensions.bzl", "doxygen_extension")
# Download doxygen version 1.10.0 on linux, default version on all other platforms
doxygen_extension.configuration(
version = "1.10.0",
sha256 = "dcfc9aa4cc05aef1f0407817612ad9e9201d9bf2ce67cecf95a024bba7d39747",
platform = "linux",
)
use_repo(doxygen_extension, "doxygen")
```
When you do so, you must also provide the SHA256 of the given doxygen archive.
If you don't know the SHA256 value, just leave it empty.
The build will fail with an error message containing the correct SHA256.
```bash
Download from https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.windows.x64.bin.zip failed: class com.google.devtools.build.lib.bazel.repository.downloader.UnrecoverableHttpException Checksum was 2135c1d5bdd6e067b3d0c40a4daac5d63d0fee1b3f4d6ef1e4f092db0d632d5b but wanted 0000000000000000000000000000000000000000000000000000000000000000
```
> [!Tip]
> Not indicating the platform will make the configuration apply to the platform it is running on.
> The build will fail when the download does not match the SHA256 checksum, i.e. when the platform changes.
> Unless you are using a system-wide doxygen installation, you should always specify the platform.
#### System-wide doxygen installation
If you set the version to `0.0.0`, the doxygen executable will be assumed to be available from the PATH.
No download will be performed and bazel will use the installed version of doxygen.
> [!Warning]
> Setting the version to `0.0.0` this will break the hermeticity of your build, as it will now depend on the environment.
#### Using a local doxygen executable
You can also provide a label pointing to the `doxygen` executable you want to use by using the `executable` parameter in the extension configuration.
No download will be performed, and the file indicated by the label will be used as the doxygen executable.
> [!Note]
> `version` and `executable` are mutually exclusive.
> You must provide exactly one of them.
### Examples
```starlark
# MODULE.bazel file
bazel_dep(name = "rules_doxygen", version = "...", dev_dependency = True)
doxygen_extension = use_extension("@rules_doxygen//:extensions.bzl", "doxygen_extension")
# Using the 1.10.0 version of Doxygen instead of the default one.
# Note that che checksum is correct only if the platform is windows.
# If the platform is different, the build will fail.
doxygen_extension.configuration(
version = "1.10.0",
sha256 = "2135c1d5bdd6e067b3d0c40a4daac5d63d0fee1b3f4d6ef1e4f092db0d632d5b",
)
use_repo(doxygen_extension, "doxygen")
```
```starlark
# MODULE.bazel file
bazel_dep(name = "rules_doxygen", version = "...", dev_dependency = True)
doxygen_extension = use_extension("@rules_doxygen//:extensions.bzl", "doxygen_extension")
doxygen_extension.configuration(
version = "1.10.0",
sha256 = "dcfc9aa4cc05aef1f0407817612ad9e9201d9bf2ce67cecf95a024bba7d39747",
platform = "linux",
)
doxygen_extension.configuration(
version = "1.10.0",
sha256 = "dcfc9aa4cc05aef1f0407817612ad9e9201d9bf2ce67cecf95a024bba7d39747",
platform = "linux-arm",
)
doxygen_extension.configuration(
version = "1.12.0",
sha256 = "6ace7dde967d41f4e293d034a67eb2c7edd61318491ee3131112173a77344001",
platform = "mac",
)
doxygen_extension.configuration(
version = "1.12.0",
sha256 = "6ace7dde967d41f4e293d034a67eb2c7edd61318491ee3131112173a77344001",
platform = "mac-arm",
)
doxygen_extension.configuration(
version = "1.11.0",
sha256 = "478fc9897d00ca181835d248a4d3e5c83c26a32d1c7571f4321ddb0f2e97459f",
platform = "windows",
)
use_repo(doxygen_extension, "doxygen")
```
```bzl
# MODULE.bazel file
bazel_dep(name = "rules_doxygen", version = "...", dev_dependency = True)
doxygen_extension = use_extension("@rules_doxygen//:extensions.bzl", "doxygen_extension")
# Download doxygen version 1.10.0 on linux
doxygen_extension.configuration(
version = "1.10.0",
sha256 = "dcfc9aa4cc05aef1f0407817612ad9e9201d9bf2ce67cecf95a024bba7d39747",
platform = "linux",
)
# Use the local doxygen installation on mac
doxygen_extension.configuration(
version = "0.0.0",
platform = "mac",
)
# Use the doxygen provided executable on mac-arm
doxygen_extension.configuration(
executable = "@my_module//path/to/doxygen:doxygen",
platform = "mac-arm",
)
# Since no configuration has been provided for them,
# all other platforms will fallback to the default version
use_repo(doxygen_extension, "doxygen")
```
""",
)