|
1 | 1 | import "build.just" |
2 | 2 |
|
3 | | -_ql_formatter := if SEMMLE_CODE != "" { '"$SEMMLE_CODE/target/intree/codeql-nolang/codeql"' } else { "codeql" } |
4 | | - |
5 | | -_py_formatter := if SEMMLE_CODE != "" { "uv run black" } else { "black" } |
6 | | - |
7 | | -_cpp_formatter := if SEMMLE_CODE != "" { "uv run clang-format" } else { "clang-format" } |
8 | | - |
9 | | -# The `buildifier` bazel target always covers the whole workspace, so the binary behind |
10 | | -# it is used instead and given paths. bazel is asked from the root of this repository, |
11 | | -# even when it sits inside another, and the files are bounded to that root as well: each |
12 | | -# repository then formats its own bazel files with the buildifier version it pins, and a |
13 | | -# verb aimed at a tree spanning both is answered once by each. This is the opposite of |
14 | | -# building, where a target needs the enclosing workspace to resolve at all, hence |
15 | | -# `_bazel` in build.just going the other way. |
16 | | -_bazel_workspace := quote(parent_directory(parent_directory(source_dir()))) |
17 | | - |
18 | | -_bazel_formatter := "bazel run --noshow_progress --ui_event_filters=,+error,+fail //misc/bazel/buildifier:binary --" |
19 | | - |
20 | | -# bazel files are named rather than suffixed, and buildifier has no exclude option of its |
21 | | -# own, so the files skipped by the target above are skipped here too. That target formats |
22 | | -# the whole workspace at once and so cannot take the path this recipe is given, which is |
23 | | -# why the two run the same binary through different entry points. Their exclusions are |
24 | | -# therefore stated twice, in two places, in two syntaxes: keep them in step, or `just |
25 | | -# format` rewrites what pre-commit and CI deliberately leave alone. |
26 | | -# |
27 | | -# bazel knows `BUILD` and `WORKSPACE` by those names and everything else by the `.bazel` |
28 | | -# extension, so a file named `BUILD.<anything>` else is a template or a generator's input |
29 | | -# rather than a bazel file, and is none of the formatter's business to parse. |
| 3 | +# These formatters take files rather than directories, so `run_on_files.py` collects them |
| 4 | +# and passes them positionally, which is what lets paths contain spaces. Both name every |
| 5 | +# file they were given, so the lines saying nothing happened are dropped: for buildifier |
| 6 | +# that is an empty warning tally, and a non-zero one is left alone, being the only notice |
| 7 | +# of what it could not fix now that the exit code is 0 either way. |
30 | 8 | # |
31 | | -# Both lists are comma-separated, so a root defining its own `_root_format` can name |
32 | | -# several patterns in one variable. It has no need to repeat the ones here: the files |
33 | | -# they cover belong to this repository, which formats them itself. Exclusions match the |
34 | | -# path as walked rather than as spelled on the command line, so one naming a directory |
35 | | -# has to cover both the path it is reached by and the path it is walked from. |
| 9 | +# The collector announces the run, rather than each recipe saying what it is about to do. |
| 10 | +# A recipe reaches that line knowing neither whether any file matched nor which of the |
| 11 | +# paths it was handed hold one, and both are worth waiting for: a path whose files are all |
| 12 | +# excluded is the ordinary case here, as a repository formatting its own bazel files is |
| 13 | +# handed a nested repository's path too. The price of letting the collector speak is that |
| 14 | +# it names the command as it is really spelled, flags that only shape output and all. It |
| 15 | +# is a report rather than something to paste in any case, the collecting being the point; |
| 16 | +# `just -n` prints what actually runs, `@` or no `@`. `_format_py` echoes its own, having |
| 17 | +# no collector to defer to and nothing to decide. |
36 | 18 | # |
37 | | -# As with the QL formatter, buildifier only names what it rewrote if it also accounts for |
38 | | -# every file it did not, so that accounting is dropped. It counts the warnings it could |
39 | | -# not fix there, which are left for linting to report rather than raised on every format. |
40 | | -_bazel_names := "BUILD,WORKSPACE,*.bazel,*.bzl,*.sky" |
41 | | - |
42 | | -_bazel_excluded := ".git/*,*/.git/*,*misc/bazel/3rdparty/*_deps/*" |
| 19 | +# `[no-cd]` keeps a relative argument meaning the caller's directory: the forwarder passes |
| 20 | +# `--justfile`, so without it the default `.` would silently become the whole repository. |
43 | 21 |
|
44 | | -_bazel_accounting := ': applied fixes, [0-9]+ warnings left$' |
45 | | - |
46 | | -# `codeql query format` and `clang-format` take files rather than directories, so the |
47 | | -# files are collected by `run_on_files.py`. Arguments are passed positionally so that |
48 | | -# paths containing spaces survive, of which this repository has many. |
49 | | -# |
50 | | -# The files that were rewritten are worth reporting, but `codeql query format` only |
51 | | -# names those once it also names every file it leaves alone, which buries them under |
52 | | -# thousands of lines. So it is asked for all of it and the lines about files it did not |
53 | | -# touch are dropped. Only those are dropped, so errors still come through, as does |
54 | | -# anything unforeseen. |
55 | | -# |
56 | | -# `[no-cd]` is what keeps a relative argument meaning the directory the caller is in. |
57 | | -# The forwarder reaches a recipe above its argument with `--justfile`, which otherwise |
58 | | -# runs it from that justfile's own directory: dropping the attribute would silently turn |
59 | | -# the default `.` into the whole repository, and the only symptom would be slowness. |
| 22 | +_ql_formatter := (if SEMMLE_CODE != "" { '"$SEMMLE_CODE/target/intree/codeql-nolang/codeql"' } else { "codeql" }) + " query format --in-place" |
60 | 23 |
|
61 | 24 | [no-cd] |
62 | 25 | [no-exit-message] |
63 | 26 | [positional-arguments] |
64 | | -_format_ql +ARGS: (_maybe_build_dist "nolang") |
65 | | - {{ cmd_sep }}{{ py }} "{{ source_dir() }}/run_on_files.py" --drop '^Formatting ' --drop '^No change for ' "*.ql,*.qll" {{ _ql_formatter }} query format --in-place -v -- "$@"{{ cmd_sep }} |
| 27 | +@_format_ql +ARGS: (_maybe_build_dist "nolang") |
| 28 | + {{ py }} "{{ source_dir() }}/run_on_files.py" --drop '^Formatting ' --drop '^No change for ' "*.ql,*.qll" {{ _ql_formatter }} -v -- "$@" |
| 29 | + |
| 30 | +_py_formatter := if SEMMLE_CODE != "" { "uv run black" } else { "black" } |
66 | 31 |
|
67 | 32 | [no-cd] |
68 | 33 | [no-exit-message] |
69 | 34 | [positional-arguments] |
70 | | -_format_py *ARGS=".": |
71 | | - {{ cmd_sep }}{{ _py_formatter }} "$@"{{ cmd_sep }} |
| 35 | +@_format_py *ARGS=".": |
| 36 | + echo "$CMD_BEGIN-> "{{ quote(_py_formatter) }}" $*$CMD_END" >&2 |
| 37 | + {{ _py_formatter }} "$@" |
| 38 | + |
| 39 | +_cpp_formatter := (if SEMMLE_CODE != "" { "uv run clang-format" } else { "clang-format" }) + " -i" |
72 | 40 |
|
73 | 41 | [no-cd] |
74 | 42 | [no-exit-message] |
75 | 43 | [positional-arguments] |
76 | | -_format_cpp *ARGS=".": |
77 | | - {{ cmd_sep }}{{ py }} "{{ source_dir() }}/run_on_files.py" "*.h,*.cpp" {{ _cpp_formatter }} -i -- "$@"{{ cmd_sep }} |
| 44 | +@_format_cpp *ARGS=".": |
| 45 | + {{ py }} "{{ source_dir() }}/run_on_files.py" "*.h,*.cpp" {{ _cpp_formatter }} -- "$@" |
| 46 | + |
| 47 | +# The `buildifier` target formats a whole workspace and so cannot take a path, so its |
| 48 | +# binary is driven directly. bazel is asked from this repository's root even when it sits |
| 49 | +# inside another, and the files are bounded to it, so each repository formats its own with |
| 50 | +# the version it pins. Building needs the enclosing workspace instead, hence `_bazel`. |
| 51 | +_bazel_workspace := quote(parent_directory(parent_directory(source_dir()))) |
| 52 | + |
| 53 | +_bazel_formatter := (if SEMMLE_CODE != "" { '"$SEMMLE_CODE/tools/bazel"' } else { "bazel" }) + " run @buildifier_prebuilt//:buildifier" |
| 54 | + |
| 55 | +# Kept out of the formatter, unlike the other three: bazel's own flags have to sit between |
| 56 | +# the target and `--`, so no single string spans both sides of it. |
| 57 | +_bazel_args := "-mode=fix -lint=fix" |
| 58 | + |
| 59 | +# Keep both in step with the `buildifier` target. The names are what it recognises walking a |
| 60 | +# workspace, a check it skips for paths handed to it, so anything extra here is rewritten |
| 61 | +# regardless. Being wider is deliberate: what it recognises differs between versions, and |
| 62 | +# each repository pins its own, so only a list covering every one of them can be shared. A |
| 63 | +# directory of one file per candidate name, formatted with `-r`, says what a version takes. |
| 64 | +# Exclusions match the path as walked and its absolute form, so one that has to hold |
| 65 | +# however the path was reached is anchored absolutely. |
| 66 | +_bazel_names := "BUILD,WORKSPACE,*.bazel,*.bzl,*.sky,*.star" |
| 67 | + |
| 68 | +_bazel_excluded := ".git/*,*/.git/*,*misc/bazel/3rdparty/*_deps/*" |
78 | 69 |
|
79 | 70 | [no-cd] |
80 | 71 | [no-exit-message] |
81 | 72 | [positional-arguments] |
82 | | -_format_bazel *ARGS=".": |
83 | | - {{ cmd_sep }}export MSYS2_ARG_CONV_EXCL="*"; {{ py }} "{{ source_dir() }}/run_on_files.py" --absolute --chdir {{ _bazel_workspace }} --within {{ _bazel_workspace }} --drop '{{ _bazel_accounting }}' --exclude '{{ _bazel_excluded }}' "{{ _bazel_names }}" {{ _bazel_formatter }} -mode=fix -lint=fix -v -- "$@"{{ cmd_sep }} |
| 73 | +@_format_bazel *ARGS=".": |
| 74 | + {{ py }} "{{ source_dir() }}/run_on_files.py" --absolute --chdir {{ _bazel_workspace }} --within {{ _bazel_workspace }} --drop ': applied fixes, 0 warnings left$' --exclude '{{ _bazel_excluded }}' "{{ _bazel_names }}" {{ _bazel_formatter }} --noshow_progress --ui_event_filters=,+error,+fail -- {{ _bazel_args }} -v -- "$@" |
0 commit comments