rules.bzl 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. # Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. # Exceptions. See /LICENSE for license information.
  3. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. """Rule to expand Bazel templates with version and build information.
  5. This rule takes a source code template and turns that into a specific source
  6. code output, substituting version information and build information from Bazel's
  7. `stable-status.txt` and `volatile-status.txt` produced by the
  8. `workpsace_status_command` during the build. When stamping is disabled, the
  9. build information is replaced with constant values to provide better caching.
  10. The template files should use Python's "template strings" syntax[1]. These rules
  11. provide a fixed set of keys whose values will be substituted, and those keys
  12. will always be substituted with something. They will have the value in the
  13. stable status file if present, otherwise the value in the volatile status file
  14. if present, otherwise the value "unknown". When reading keys from the status
  15. files, a prefix of `STABLE_` will be removed from the key if present.
  16. [1]: https://docs.python.org/3/library/string.html#template-strings
  17. The substituted keys, and any guidance on values:
  18. - `VERSION` (the version string for Carbon)
  19. - `BUILD_EMBED_LABEL` (value of --embed_label)
  20. - `BUILD_HOST` (the name of the host machine running the build)
  21. - `BUILD_USER` (the name of the user running the build)
  22. - `GIT_COMMIT_SHA` (output of `git parse-rev --short HEAD` or `unknown`)
  23. - `GIT_DIRTY_SUFFIX` (`.dirty` if dirty client state or `` if unknown)
  24. - `BUILD_TIMESTAMP` (the time of the build in seconds since the Unix Epoch)
  25. - `ATTRIBUTE` (an optional attribute to apply to definitions, defaults to empty)
  26. """
  27. load(":compute_version.bzl", "compute_version")
  28. _STAMP_DOC = """
  29. Follows behavior of the common 'stamp' attributes on rules. Set to 1 or 0 to
  30. force stamping with actual build info on or off respectively, and to -1 to
  31. follow the value of the command line flag `--stamp`.
  32. """
  33. def _is_exec_config(ctx):
  34. """Detect if this is the exec configuration, previously known as "host".
  35. Sadly, there is not yet a supported way to detect this so replicate the
  36. hacks others currently use. Bazel issue:
  37. https://github.com/bazelbuild/bazel/issues/14444
  38. """
  39. return "-exec" in ctx.bin_dir.path or "/host/" in ctx.bin_dir.path
  40. def _expand_version_build_info_impl(ctx):
  41. """Generates a file from a template, substituting version and build info."""
  42. inputs = [ctx.file.template]
  43. # The substitutions provided and their default values.
  44. substitutions = {
  45. "BUILD_EMBED_LABEL": "unknown",
  46. "BUILD_HOST": "unknown",
  47. "BUILD_TIMESTAMP": "unknown",
  48. "BUILD_USER": "unknown",
  49. "GIT_COMMIT_SHA": "unknown",
  50. "GIT_DIRTY_SUFFIX": "",
  51. "MAKE_WEAK": "0",
  52. "VERSION": compute_version(ctx),
  53. }
  54. substitutions.update(ctx.attr.substitutions)
  55. arguments = [
  56. "--template=" + ctx.file.template.path,
  57. "--output=" + ctx.outputs.out.path,
  58. ] + [
  59. "--substitution=" + key + "=" + value
  60. for key, value in substitutions.items()
  61. ]
  62. # We only want to allow stamping outside of the exec configuration.
  63. if not _is_exec_config(ctx):
  64. # Look at the attribute.
  65. stamp = ctx.attr.stamp
  66. # If requested, use the command line flag to select.
  67. if stamp == -1:
  68. # Set the default from `--stamp` / `--nostamp` command line flag,
  69. # which we detect through a macro and `config_setting`, and pipe
  70. # through an attribute.
  71. stamp = 1 if ctx.attr.internal_stamp_flag_detect else 0
  72. # Add the status files if stamping.
  73. if stamp == 1:
  74. inputs += [
  75. ctx.info_file,
  76. ctx.version_file,
  77. ]
  78. arguments += [
  79. "--status-file=" + ctx.info_file.path,
  80. "--status-file=" + ctx.version_file.path,
  81. ]
  82. ctx.actions.run(
  83. inputs = inputs,
  84. outputs = [ctx.outputs.out],
  85. executable = ctx.executable._gen_tmpl_tool,
  86. arguments = arguments,
  87. progress_message = "Generating templated source file: " +
  88. ctx.outputs.out.short_path,
  89. )
  90. expand_version_build_info_internal = rule(
  91. implementation = _expand_version_build_info_impl,
  92. attrs = {
  93. "internal_stamp_flag_detect": attr.bool(default = False),
  94. "out": attr.output(mandatory = True),
  95. "stamp": attr.int(values = [-1, 0, 1], default = -1, doc = _STAMP_DOC),
  96. "substitutions": attr.string_dict(
  97. doc = "Extra substitutions, potentially overriding defaults.",
  98. ),
  99. "template": attr.label(
  100. allow_single_file = True,
  101. ),
  102. "_alpha_number_flag": attr.label(default = ":alpha_number"),
  103. "_beta_number_flag": attr.label(default = ":beta_number"),
  104. "_gen_tmpl_tool": attr.label(
  105. default = Label("//bazel/version:gen_tmpl"),
  106. allow_files = True,
  107. executable = True,
  108. cfg = "exec",
  109. ),
  110. "_nightly_date_flag": attr.label(default = ":nightly_date"),
  111. "_pre_release_flag": attr.label(default = ":pre_release"),
  112. "_rc_number_flag": attr.label(default = ":rc_number"),
  113. "_release_flag": attr.label(default = ":release"),
  114. },
  115. )
  116. # We need a macro wrapping the rule so that we can inject a select that observes
  117. # the `--stamp` command-line value and use that when needed.
  118. def expand_version_build_info(name, **kwargs):
  119. expand_version_build_info_internal(
  120. name = name,
  121. internal_stamp_flag_detect = select({
  122. "//bazel/version:internal_stamp_flag_detect": True,
  123. "//conditions:default": False,
  124. }),
  125. **kwargs
  126. )