1#!/bin/bash
2
3# --- begin runfiles.bash initialization ---
4# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
5set -euo pipefail
6if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
7 if [[ -f "$0.runfiles_manifest" ]]; then
8 export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
9 elif [[ -f "$0.runfiles/MANIFEST" ]]; then
10 export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
11 elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
12 export RUNFILES_DIR="$0.runfiles"
13 fi
14fi
15if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
16 source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
17elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
18 source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
19 "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
20else
21 echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
22 exit 1
23fi
24# --- end runfiles.bash initialization ---
25
26die () {
27 echo "$1" 1>&2
28 exit 1
29}
30
31[[ "$1" =~ external/* ]] && buildifier="${{1#external/}}" || buildifier="$TEST_WORKSPACE/$1"
32[[ "$2" =~ external/* ]] && buildifier2="${{2#external/}}" || buildifier2="$TEST_WORKSPACE/$2"
33buildifier="$(rlocation "$buildifier")"
34buildifier2="$(rlocation "$buildifier2")"
35
36touch WORKSPACE.bazel
37[[ -d test_dir ]] && rm -r test_dir
38mkdir -p test_dir/subdir
39mkdir -p golden
40INPUT="load(':foo.bzl', 'foo'); foo(tags=['b', 'a'],srcs=['d', 'c'])" # formatted differently in build and bzl modes
41echo -e "$INPUT" > test_dir/BUILD
42echo -e "$INPUT" > test_dir/test.bzl
43echo -e "$INPUT" > test_dir/subdir/test.bzl
44echo -e "$INPUT" > test_dir/subdir/build # lowercase, should be ignored by -r
45echo -e "$INPUT" > test.bzl # outside the test_dir directory
46echo -e "$INPUT" > test2.bzl # outside the test_dir directory
47echo -e "not valid +" > test_dir/foo.bar
48echo -e '{ "type": "build" }' > test_dir/.buildifier.test.json # demonstrate config file works by overriding input type to format a bzl file as a BUILD file.
49mkdir test_dir/workspace # name of a starlark file, but a directory
50mkdir test_dir/.git # contents should be ignored
51echo -e "a+b" > test_dir/.git/git.bzl
52cat > test_dir/MODULE.bazel <<'EOF'
53module(name='my-module',version='1.0',compatibility_level=1)
54bazel_dep(name='rules_cc',version='0.0.1')
55bazel_dep(name='protobuf',repo_name='com_google_protobuf',version='3.19.0')
56bazel_dep(
57 name='rules_go',
58 version='0.37.0',
59 repo_name='io_bazel_rules_go',
60)
61go_sdk=use_extension("@io_bazel_rules_go//go:extensions.bzl","go_sdk")
62# Known to exist since it is instantiated by rules_go itself.
63use_repo(go_sdk,"go_default_sdk")
64non_module_deps = use_extension("//internal/bzlmod:non_module_deps.bzl","non_module_deps")
65use_repo(
66 non_module_deps,
67 "bazel_gazelle_go_repository_tools",
68 "bazel_gazelle_go_repository_config",
69 "bazel_gazelle_go_repository_cache",
70)
71rules_go_non_module_deps = use_extension("@io_bazel_rules_go//go/private:extensions.bzl","non_module_dependencies",dev_dependency=True)
72use_repo(rules_go_non_module_deps,"go_googleapis")
73go_deps = use_extension("//:extensions.bzl", "go_deps")
74go_deps.from_file(go_mod = "//:go.mod")
75use_repo(
76 go_deps,
77 "com_github_fsnotify_fsnotify",
78 "com_github_fsnotify_fsnotify",
79 "com_github_bmatcuk_doublestar_v4",
80 "com_github_bazelbuild_buildtools",
81 "com_github_google_go_cmp",
82 "com_github_pelletier_go_toml",
83 "org_golang_x_mod",
84 "com_github_pmezard_go_difflib",
85 # Separated by comment.
86 "org_golang_x_sync",
87 "org_golang_x_tools",
88 # Used internally by the go_deps module extension.
89 "bazel_gazelle_go_repository_directives",
90 c = "a",
91 b = "b",
92 a = "c",
93)
94bazel_dep(name="foo",version="1.0")
95git_override(module_name="foo",remote="foo.git",commit="1234567890")
96bazel_dep(name="bar",version="1.0")
97archive_override(module_name="not_bar",integrity="sha256-1234567890")
98# do not sort
99use_repo(go_deps, "b", "b", "a")
100use_repo(
101 # do not sort
102 go_deps,
103 "b",
104 "b",
105 "a",
106)
107use_repo(
108 go_deps,
109 # do not sort
110 "b",
111 "b",
112 "a",
113)
114
115bazel_dep(name='prod_dep',version='3.19.0')
116bazel_dep(name='other_prod_dep',version='3.19.0',dev_dependency=False)
117bazel_dep(name='dev_dep',version='3.19.0',dev_dependency=True)
118bazel_dep(name = "weird_dep", version = "3.19.0", dev_dependency = "True" == "True")
119bazel_dep(name='yet_another_prod_dep',version='3.19.0')
120EOF
121
122cp test_dir/foo.bar golden/foo.bar
123cp test_dir/subdir/build golden/build
124cp test_dir/.git/git.bzl golden/git.bzl
125
126"$buildifier" < test_dir/BUILD > stdout
127"$buildifier" -r test_dir
128"$buildifier" test.bzl
129"$buildifier" --path=foo.bzl test2.bzl
130"$buildifier" --config=test_dir/.buildifier.test.json < test_dir/test.bzl > test_dir/test.bzl.BUILD.out
131"$buildifier" --config=example > test_dir/.buildifier.example.json
132"$buildifier2" test_dir/test.bzl > test_dir/test.bzl.out
133
134cat > golden/BUILD.golden <<EOF
135load(":foo.bzl", "foo")
136
137foo(
138 srcs = [
139 "c",
140 "d",
141 ],
142 tags = [
143 "a",
144 "b",
145 ],
146)
147EOF
148
149cat > golden/test.bzl.golden <<EOF
150load(":foo.bzl", "foo")
151
152foo(tags = ["b", "a"], srcs = ["d", "c"])
153EOF
154
155cat > golden/MODULE.bazel.golden <<EOF
156module(
157 name = "my-module",
158 version = "1.0",
159 compatibility_level = 1,
160)
161
162bazel_dep(name = "rules_cc", version = "0.0.1")
163bazel_dep(name = "protobuf", version = "3.19.0", repo_name = "com_google_protobuf")
164bazel_dep(
165 name = "rules_go",
166 version = "0.37.0",
167 repo_name = "io_bazel_rules_go",
168)
169
170go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")
171
172# Known to exist since it is instantiated by rules_go itself.
173use_repo(go_sdk, "go_default_sdk")
174
175non_module_deps = use_extension("//internal/bzlmod:non_module_deps.bzl", "non_module_deps")
176use_repo(
177 non_module_deps,
178 "bazel_gazelle_go_repository_cache",
179 "bazel_gazelle_go_repository_config",
180 "bazel_gazelle_go_repository_tools",
181)
182
183rules_go_non_module_deps = use_extension("@io_bazel_rules_go//go/private:extensions.bzl", "non_module_dependencies", dev_dependency = True)
184use_repo(rules_go_non_module_deps, "go_googleapis")
185
186go_deps = use_extension("//:extensions.bzl", "go_deps")
187go_deps.from_file(go_mod = "//:go.mod")
188use_repo(
189 go_deps,
190 "com_github_bazelbuild_buildtools",
191 "com_github_bmatcuk_doublestar_v4",
192 "com_github_fsnotify_fsnotify",
193 "com_github_google_go_cmp",
194 "com_github_pelletier_go_toml",
195 "com_github_pmezard_go_difflib",
196 "org_golang_x_mod",
197 # Separated by comment.
198 "org_golang_x_sync",
199 "org_golang_x_tools",
200 # Used internally by the go_deps module extension.
201 "bazel_gazelle_go_repository_directives",
202 a = "c",
203 b = "b",
204 c = "a",
205)
206
207bazel_dep(name = "foo", version = "1.0")
208git_override(
209 module_name = "foo",
210 commit = "1234567890",
211 remote = "foo.git",
212)
213
214bazel_dep(name = "bar", version = "1.0")
215
216archive_override(
217 module_name = "not_bar",
218 integrity = "sha256-1234567890",
219)
220
221# do not sort
222use_repo(go_deps, "b", "a")
223use_repo(
224 # do not sort
225 go_deps,
226 "b",
227 "a",
228)
229use_repo(
230 go_deps,
231 # do not sort
232 "b",
233 "a",
234)
235
236bazel_dep(name = "prod_dep", version = "3.19.0")
237bazel_dep(name = "other_prod_dep", version = "3.19.0", dev_dependency = False)
238
239bazel_dep(name = "dev_dep", version = "3.19.0", dev_dependency = True)
240bazel_dep(name = "weird_dep", version = "3.19.0", dev_dependency = "True" == "True")
241
242bazel_dep(name = "yet_another_prod_dep", version = "3.19.0")
243EOF
244
245cat > golden/.buildifier.example.json <<EOF
246{
247 "type": "auto",
248 "mode": "fix",
249 "lint": "fix",
250 "warningsList": [
251 "attr-applicable_licenses",
252 "attr-cfg",
253 "attr-license",
254 "attr-licenses",
255 "attr-non-empty",
256 "attr-output-default",
257 "attr-single-file",
258 "build-args-kwargs",
259 "bzl-visibility",
260 "confusing-name",
261 "constant-glob",
262 "ctx-actions",
263 "ctx-args",
264 "deprecated-function",
265 "depset-items",
266 "depset-iteration",
267 "depset-union",
268 "dict-concatenation",
269 "dict-method-named-arg",
270 "duplicated-name",
271 "filetype",
272 "function-docstring",
273 "function-docstring-args",
274 "function-docstring-header",
275 "function-docstring-return",
276 "git-repository",
277 "http-archive",
278 "integer-division",
279 "keyword-positional-params",
280 "list-append",
281 "load",
282 "module-docstring",
283 "name-conventions",
284 "native-android",
285 "native-build",
286 "native-cc",
287 "native-java",
288 "native-package",
289 "native-proto",
290 "native-py",
291 "no-effect",
292 "output-group",
293 "overly-nested-depset",
294 "package-name",
295 "package-on-top",
296 "positional-args",
297 "print",
298 "provider-params",
299 "redefined-variable",
300 "repository-name",
301 "return-value",
302 "rule-impl-return",
303 "skylark-comment",
304 "skylark-docstring",
305 "string-iteration",
306 "uninitialized",
307 "unnamed-macro",
308 "unreachable",
309 "unsorted-dict-items",
310 "unused-variable"
311 ]
312}
313EOF
314
315diff test_dir/BUILD golden/BUILD.golden
316diff test_dir/test.bzl golden/test.bzl.golden
317diff test_dir/subdir/test.bzl golden/test.bzl.golden
318diff test_dir/test.bzl.BUILD.out golden/BUILD.golden
319diff test_dir/subdir/build golden/build
320diff test_dir/foo.bar golden/foo.bar
321diff test.bzl golden/test.bzl.golden
322diff test2.bzl golden/test.bzl.golden
323diff stdout golden/test.bzl.golden
324diff test_dir/test.bzl.out golden/test.bzl.golden
325diff test_dir/.git/git.bzl golden/git.bzl
326diff test_dir/MODULE.bazel golden/MODULE.bazel.golden
327diff test_dir/.buildifier.example.json golden/.buildifier.example.json
328
329# Test run on a directory without -r
330"$buildifier" test_dir || ret=$?
331if [[ $ret -ne 3 ]]; then
332 die "Directory without -r: expected buildifier to exit with 3, actual: $ret"
333fi
334
335# Test the linter
336
337cat > test_dir/to_fix.bzl <<EOF
338load("//foo/bar/internal/baz:module.bzl", "b")
339
340b()
341a = 1 / 2
342d = {"b": 2, "a": 1}
343attr.foo(bar, cfg = "data")
344EOF
345
346cat > test_dir/fixed_golden.bzl <<EOF
347load("//foo/bar/internal/baz:module.bzl", "b")
348
349b()
350a = 1 // 2
351d = {"b": 2, "a": 1}
352attr.foo(bar)
353EOF
354
355cat > test_dir/fixed_golden_all.bzl <<EOF
356load("//foo/bar/internal/baz:module.bzl", "b")
357
358b()
359a = 1 // 2
360d = {"a": 1, "b": 2}
361attr.foo(bar)
362EOF
363
364cat > test_dir/fixed_golden_dict_cfg.bzl <<EOF
365load("//foo/bar/internal/baz:module.bzl", "b")
366
367b()
368a = 1 / 2
369d = {"a": 1, "b": 2}
370attr.foo(bar)
371EOF
372
373cat > test_dir/fixed_golden_cfg.bzl <<EOF
374load("//foo/bar/internal/baz:module.bzl", "b")
375
376b()
377a = 1 / 2
378d = {"b": 2, "a": 1}
379attr.foo(bar)
380EOF
381
382cat > test_dir/fix_report_golden <<EOF
383test_dir/to_fix_tmp.bzl: applied fixes, 2 warnings left
384fixed test_dir/to_fix_tmp.bzl
385EOF
386
387error_bzl="test_dir/to_fix_tmp.bzl:1: bzl-visibility: Module \"//foo/bar/internal/baz:module.bzl\" can only be loaded from files located inside \"//foo/bar\", not from \"//test_dir/to_fix_tmp.bzl\". (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#bzl-visibility)"
388error_docstring="test_dir/to_fix_tmp.bzl:1: module-docstring: The file has no module docstring."$'\n'"A module docstring is a string literal (not a comment) which should be the first statement of a file (it may follow comment lines). (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#module-docstring)"
389error_integer="test_dir/to_fix_tmp.bzl:4: integer-division: The \"/\" operator for integer division is deprecated in favor of \"//\". (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#integer-division)"
390error_dict="test_dir/to_fix_tmp.bzl:5: unsorted-dict-items: Dictionary items are out of their lexicographical order. (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#unsorted-dict-items)"
391error_cfg="test_dir/to_fix_tmp.bzl:6: attr-cfg: cfg = \"data\" for attr definitions has no effect and should be removed. (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#attr-cfg)"
392
393test_lint () {
394 ret=0
395 cp test_dir/to_fix.bzl test_dir/to_fix_tmp.bzl
396 echo "$4" > golden/error_golden
397 echo "${4//test_dir/another_test_dir}" > golden/error_golden_another
398
399 cat > golden/fix_report_golden <<EOF
400test_dir/to_fix_tmp.bzl: applied fixes, $5 warnings left
401fixed test_dir/to_fix_tmp.bzl
402EOF
403
404 # --lint=warn with --mode=check
405 $buildifier --mode=check --lint=warn $2 test_dir/to_fix_tmp.bzl 2> test_dir/error || ret=$?
406 if [[ $ret -ne 4 ]]; then
407 die "$1: warn: Expected buildifier to exit with 4, actual: $ret"
408 fi
409 diff test_dir/error golden/error_golden || die "$1: wrong console output for --mode=check --lint=warn"
410 diff test_dir/to_fix.bzl test_dir/to_fix.bzl || die "$1: --mode=check --lint=warn shouldn't modify files"
411
412 # --lint=warn
413 $buildifier --lint=warn $2 test_dir/to_fix_tmp.bzl 2> test_dir/error || ret=$?
414 if [[ $ret -ne 4 ]]; then
415 die "$1: warn: Expected buildifier to exit with 4, actual: $ret"
416 fi
417 diff test_dir/error golden/error_golden || die "$1: wrong console output for --lint=warn"
418
419 # --lint=warn with --path
420 $buildifier --lint=warn --path=another_test_dir/to_fix_tmp.bzl $2 test_dir/to_fix_tmp.bzl 2> test_dir/error || ret=$?
421 if [[ $ret -ne 4 ]]; then
422 die "$1: warn: Expected buildifier to exit with 4, actual: $ret"
423 fi
424 diff test_dir/error golden/error_golden_another || die "$1: wrong console output for --lint=warn and --path"
425
426 # --lint=fix
427 $buildifier --lint=fix $2 -v test_dir/to_fix_tmp.bzl 2> test_dir/fix_report || ret=$?
428 if [[ $ret -ne 4 ]]; then
429 die "$1: fix: Expected buildifier to exit with 4, actual: $ret"
430 fi
431 diff test_dir/to_fix_tmp.bzl $3 || die "$1: wrong file output for --lint=fix"
432 diff test_dir/fix_report golden/fix_report_golden || die "$1: wrong console output for --lint=fix"
433}
434
435test_lint "default" "" "test_dir/fixed_golden.bzl" "$error_bzl"$'\n'"$error_docstring"$'\n'"$error_integer"$'\n'"$error_cfg" 2
436test_lint "all" "--warnings=all" "test_dir/fixed_golden_all.bzl" "$error_bzl"$'\n'"$error_docstring"$'\n'"$error_integer"$'\n'"$error_dict"$'\n'"$error_cfg" 2
437test_lint "cfg" "--warnings=attr-cfg" "test_dir/fixed_golden_cfg.bzl" "$error_cfg" 0
438test_lint "custom" "--warnings=-bzl-visibility,-integer-division,+unsorted-dict-items" "test_dir/fixed_golden_dict_cfg.bzl" "$error_docstring"$'\n'"$error_dict"$'\n'"$error_cfg" 1
439
440# Test --format=json
441
442mkdir test_dir/json
443cp test_dir/to_fix.bzl test_dir/json
444
445# just not formatted
446cat > test_dir/json/to_fix_2.bzl <<EOF
447a=b
448EOF
449
450# not formatted with rewrites
451cat > test_dir/json/to_fix_3.bzl <<EOF
452x = 0123
453EOF
454
455# formatted, no warnings
456cat > test_dir/json/to_fix_4.bzl <<EOF
457a = b
458EOF
459
460# not a starlark file
461cat > test_dir/json/foo.bar <<EOF
462this is not a starlark file
463EOF
464
465cat > golden/json_report_golden <<EOF
466{
467 "success": false,
468 "files": [
469 {
470 "filename": "to_fix.bzl",
471 "formatted": true,
472 "valid": true,
473 "warnings": [
474 {
475 "start": {
476 "line": 1,
477 "column": 6
478 },
479 "end": {
480 "line": 1,
481 "column": 41
482 },
483 "category": "bzl-visibility",
484 "actionable": true,
485 "autoFixable": false,
486 "message": "Module \"//foo/bar/internal/baz:module.bzl\" can only be loaded from files located inside \"//foo/bar\", not from \"//test_dir/json/to_fix.bzl\".",
487 "url": "https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#bzl-visibility"
488 },
489 {
490 "start": {
491 "line": 4,
492 "column": 5
493 },
494 "end": {
495 "line": 4,
496 "column": 10
497 },
498 "category": "integer-division",
499 "actionable": true,
500 "autoFixable": true,
501 "message": "The \"/\" operator for integer division is deprecated in favor of \"//\".",
502 "url": "https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#integer-division"
503 },
504 {
505 "start": {
506 "line": 6,
507 "column": 15
508 },
509 "end": {
510 "line": 6,
511 "column": 27
512 },
513 "category": "attr-cfg",
514 "actionable": true,
515 "autoFixable": true,
516 "message": "cfg = \"data\" for attr definitions has no effect and should be removed.",
517 "url": "https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#attr-cfg"
518 }
519 ]
520 },
521 {
522 "filename": "to_fix_2.bzl",
523 "formatted": false,
524 "valid": true,
525 "warnings": []
526 },
527 {
528 "filename": "to_fix_3.bzl",
529 "formatted": false,
530 "valid": true,
531 "warnings": []
532 },
533 {
534 "filename": "to_fix_4.bzl",
535 "formatted": true,
536 "valid": true,
537 "warnings": []
538 }
539 ]
540}
541EOF
542
543cat > golden/json_report_small_golden <<EOF
544{
545 "success": true,
546 "files": [
547 {
548 "filename": "to_fix_4.bzl",
549 "formatted": true,
550 "valid": true,
551 "warnings": []
552 }
553 ]
554}
555EOF
556
557cat > golden/json_report_stdin_golden <<EOF
558{
559 "success": true,
560 "files": [
561 {
562 "filename": "\u003cstdin\u003e",
563 "formatted": true,
564 "valid": true,
565 "warnings": []
566 }
567 ]
568}
569EOF
570
571cat > golden/json_report_invalid_file_golden <<EOF
572{
573 "success": false,
574 "files": [
575 {
576 "filename": "to_fix_4.bzl",
577 "formatted": true,
578 "valid": true,
579 "warnings": []
580 },
581 {
582 "filename": "foo.bar",
583 "formatted": false,
584 "valid": false,
585 "warnings": []
586 }
587 ]
588}
589EOF
590
591cd test_dir/json
592
593$buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v to_fix.bzl to_fix_2.bzl to_fix_3.bzl to_fix_4.bzl > json_report
594diff json_report ../../golden/json_report_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with many files"
595
596$buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v to_fix_4.bzl > json_report
597diff json_report ../../golden/json_report_small_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with a single file"
598
599$buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v < to_fix_4.bzl > json_report
600diff json_report ../../golden/json_report_stdin_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with stdin"
601
602$buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v to_fix_4.bzl foo.bar > json_report
603diff json_report ../../golden/json_report_invalid_file_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with an invalid file"
604
605cd ../..
606
607# Test the multifile functionality
608
609mkdir multifile
610cd multifile
611
612cat > lib.bzl <<EOF
613def foo():
614 """
615 This is a function foo.
616
617 Please use it in favor of the
618 deprecated function bar
619 """
620 pass
621
622def bar():
623 """
624 This is a function bar.
625
626 Deprecated:
627 please use foo instead.
628 """
629 pass
630EOF
631
632touch WORKSPACE
633
634cat > BUILD <<EOF
635load(":lib.bzl", "foo", "bar")
636load(":nonexistent.bzl", "foo2", "bar2")
637EOF
638
639cat > report_golden <<EOF
640BUILD:1: deprecated-function: The function "bar" defined in "//lib.bzl" is deprecated. (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#deprecated-function)
641EOF
642
643$buildifier --lint=warn --warnings=deprecated-function BUILD 2> report || ret=$?
644diff report_golden report || die "$1: wrong console output for multifile warnings (WORKSPACE exists)"
View as plain text