symlink_helpers.bzl 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. """Rules for symlinking in ways that assist install_filegroups."""
  5. def _symlink_file_impl(ctx):
  6. executable = None
  7. if ctx.attr.symlink_binary:
  8. out = ctx.actions.declare_file(ctx.label.name)
  9. ctx.actions.symlink(
  10. output = out,
  11. target_file = ctx.file.symlink_binary,
  12. is_executable = True,
  13. )
  14. executable = out
  15. elif ctx.attr.symlink_label:
  16. out = ctx.actions.declare_file(ctx.label.name)
  17. ctx.actions.symlink(
  18. output = out,
  19. target_file = ctx.file.symlink_label,
  20. )
  21. else:
  22. fail("Missing symlink target")
  23. return [
  24. DefaultInfo(
  25. executable = executable,
  26. files = depset(direct = [out]),
  27. default_runfiles = ctx.runfiles(files = [out]),
  28. ),
  29. ]
  30. symlink_file = rule(
  31. doc = "Symlinks a single file, with support for multiple approaches.",
  32. implementation = _symlink_file_impl,
  33. attrs = {
  34. "symlink_binary": attr.label(
  35. allow_single_file = True,
  36. executable = True,
  37. cfg = "target",
  38. ),
  39. "symlink_label": attr.label(allow_single_file = True),
  40. },
  41. )
  42. def _symlink_filegroup_impl(ctx):
  43. prefix = ctx.attr.out_prefix
  44. remove_prefix = ctx.attr.remove_prefix
  45. outputs = []
  46. for f in ctx.files.srcs:
  47. # We normalize the path to be package-relative in order to ensure
  48. # consistent paths across possible repositories. After experimenting,
  49. # incrementally removing these path fragments, in this order,
  50. # effectively handles the full permutation of files across different
  51. # repositories and both generated and source files.
  52. relative_path = f.path
  53. relative_path = relative_path.removeprefix(f.root.path + "/")
  54. relative_path = relative_path.removeprefix(f.owner.workspace_root + "/")
  55. relative_path = relative_path.removeprefix(f.owner.package + "/")
  56. if not relative_path.startswith(remove_prefix):
  57. fail("Missing `{0}` in `{1}`".format(remove_prefix, relative_path))
  58. relative_path = relative_path.removeprefix(remove_prefix)
  59. out = ctx.actions.declare_file(prefix + "/" + relative_path)
  60. outputs.append(out)
  61. ctx.actions.symlink(output = out, target_file = f)
  62. if len(ctx.files.srcs) != len(outputs):
  63. fail("Output count mismatch!")
  64. return [
  65. DefaultInfo(
  66. files = depset(direct = outputs),
  67. default_runfiles = ctx.runfiles(files = outputs),
  68. ),
  69. ]
  70. symlink_filegroup = rule(
  71. doc = "Symlinks an entire filegroup, preserving its structure",
  72. implementation = _symlink_filegroup_impl,
  73. attrs = {
  74. "out_prefix": attr.string(mandatory = True),
  75. "remove_prefix": attr.string(),
  76. "srcs": attr.label_list(mandatory = True),
  77. },
  78. )