1"""
2This module contains factory methods for simple rule and implementation generation
3"""
4
5load("@bazel_skylib//lib:shell.bzl", "shell")
6
7# buildifier: disable=print
8def _value_deprecation(ctx, attr, value):
9 """
10 Prints a deprecation message related to a specific value for an attr.
11
12 Args:
13 ctx: The execution context
14 attr: A String representing the attribute name
15 value: The deprecated value
16 """
17 print("DEPRECATION NOTICE: value '%s' for attribute '%s' will be removed in the future. Migrate '%s' to buildifier_test." % (value, attr, ctx.label))
18
19# buildifier: disable=print
20def _attr_deprecation(ctx, attr):
21 """
22 Prints an attribute deprecation message.
23
24 Args:
25 ctx: The execution context
26 attr: A String representing the deprecated attribute name
27 """
28 print("DEPRECATION NOTICE: attribute '%s' will be removed in the future. Migrate '%s' to buildifier_test." % (attr, ctx.label))
29
30def buildifier_attr_factory(test_rule = False):
31 """
32 Helper macro to generate a struct of attrs for use in a rule() definition.
33
34 Args:
35 test_rule: Whether or not to generate attrs for a test rule.
36
37 Returns:
38 A dictionary of attributes relevant to the rule
39 """
40 attrs = {
41 "buildifier": attr.label(
42 default = "//buildifier",
43 cfg = "exec",
44 executable = True,
45 ),
46 "verbose": attr.bool(
47 doc = "Print verbose information on standard error",
48 ),
49 "exclude_patterns": attr.string_list(
50 allow_empty = True,
51 doc = "A list of glob patterns passed to the find command. E.g. './vendor/*' to exclude the Go vendor directory. In test rules, this attribute requires the use of the no_sandbox attribute.",
52 ),
53 "mode": attr.string(
54 default = "fix" if not test_rule else "diff",
55 doc = "Formatting mode",
56 values = ["check", "diff", "print_if_changed"] + ["fix"] if not test_rule else [],
57 ),
58 "lint_mode": attr.string(
59 doc = "Linting mode",
60 values = ["", "warn"] + ["fix"] if not test_rule else [],
61 ),
62 "lint_warnings": attr.string_list(
63 allow_empty = True,
64 doc = "all prefixed with +/- if you want to include in or exclude from the default set of warnings, or none prefixed with +/- if you want to override the default set, or 'all' for all available warnings",
65 ),
66 "diff_command": attr.string(
67 doc = "Command to use to show diff, with mode=diff. E.g. 'diff -u'",
68 ),
69 "multi_diff": attr.bool(
70 default = False,
71 doc = "Set to True if the diff command specified by the 'diff_command' can diff multiple files in the style of 'tkdiff'",
72 ),
73 "add_tables": attr.label(
74 mandatory = False,
75 doc = "path to JSON file with custom table definitions which will be merged with the built-in tables",
76 allow_single_file = True,
77 ),
78 "_runner": attr.label(
79 default = "//buildifier:runner.bash.template",
80 allow_single_file = True,
81 ),
82 "_windows_runner": attr.label(
83 default = "@com_github_bazelbuild_buildtools//buildifier:runner.bat.template",
84 allow_single_file = True,
85 ),
86 }
87
88 if test_rule:
89 attrs.update({
90 "srcs": attr.label_list(
91 allow_files = [
92 ".bazel",
93 ".bzl",
94 ".oss",
95 ".sky",
96 "BUILD",
97 "WORKSPACE",
98 "WORKSPACE.bzlmod",
99 ],
100 doc = "A list of labels representing the starlark files to include in the test",
101 ),
102 "no_sandbox": attr.bool(
103 default = False,
104 doc = "Set to True to enable running buildifier on all files in the workspace",
105 ),
106 "workspace": attr.label(
107 allow_single_file = True,
108 doc = "Label of the WORKSPACE file; required when the no-sandbox attribute is True",
109 ),
110 })
111
112 return attrs
113
114def buildifier_impl_factory(ctx, test_rule = False):
115 """
116 Helper macro to generate a buildifier or buildifier_test rule.
117
118 This macro does not depend on defaults encoded in the binary, instead
119 preferring to set explicit values for each flag.
120
121 Args:
122 ctx: The execution context.
123 test_rule: Whether or not to generate a test rule.
124
125 Returns:
126 A DefaultInfo provider
127 """
128
129 if not test_rule and ctx.attr.mode in ["check", "diff", "print_if_changed"]:
130 _value_deprecation(ctx, "mode", ctx.attr.mode)
131
132 args = [
133 "-mode=%s" % ctx.attr.mode,
134 "-v=%s" % str(ctx.attr.verbose).lower(),
135 ]
136
137 if ctx.attr.lint_mode:
138 args.append("-lint=%s" % ctx.attr.lint_mode)
139
140 if ctx.attr.lint_warnings:
141 if not ctx.attr.lint_mode:
142 fail("Cannot pass 'lint_warnings' without a 'lint_mode'")
143 args.append("--warnings={}".format(",".join(ctx.attr.lint_warnings)))
144
145 if ctx.attr.multi_diff:
146 args.append("-multi_diff")
147 if not test_rule:
148 _attr_deprecation(ctx, "multi_diff")
149
150 if ctx.attr.diff_command:
151 args.append("-diff_command=%s" % ctx.attr.diff_command)
152 if not test_rule:
153 _attr_deprecation(ctx, "diff_command")
154
155 if ctx.attr.add_tables:
156 args.append("-add_tables=%s" % ctx.file.add_tables.path)
157
158 exclude_patterns_str = ""
159 if ctx.attr.exclude_patterns:
160 if test_rule and not ctx.attr.no_sandbox:
161 fail("Cannot use 'exclude_patterns' in a test rule without 'no_sandbox'")
162 exclude_patterns = ["\\! -path %s" % shell.quote(pattern) for pattern in ctx.attr.exclude_patterns]
163 exclude_patterns_str = " ".join(exclude_patterns)
164
165 workspace = ""
166 if test_rule and ctx.attr.no_sandbox:
167 if not ctx.file.workspace:
168 fail("Cannot use 'no_sandbox' without a 'workspace'")
169 workspace = ctx.file.workspace.path
170
171 out_file = ctx.actions.declare_file(ctx.label.name + ".bash")
172
173 substitutions = {
174 "@@ARGS@@": shell.array_literal(args),
175 "@@BUILDIFIER_SHORT_PATH@@": shell.quote(ctx.executable.buildifier.short_path),
176 "@@EXCLUDE_PATTERNS@@": exclude_patterns_str,
177 "@@WORKSPACE@@": workspace,
178 }
179
180 if ctx.executable.buildifier.extension.lower() == "exe":
181 out_file = ctx.actions.declare_file(ctx.label.name + ".bat")
182 runner_template = ctx.file._windows_runner
183 else:
184 out_file = ctx.actions.declare_file(ctx.label.name + ".bash")
185 runner_template = ctx.file._runner
186
187 ctx.actions.expand_template(
188 template = runner_template,
189 output = out_file,
190 substitutions = substitutions,
191 is_executable = True,
192 )
193
194 runfiles = [ctx.executable.buildifier]
195 if test_rule:
196 runfiles.extend(ctx.files.srcs)
197 if ctx.attr.no_sandbox:
198 runfiles.append(ctx.file.workspace)
199
200 return DefaultInfo(
201 files = depset([out_file]),
202 runfiles = ctx.runfiles(files = runfiles),
203 executable = out_file,
204 )
View as plain text