Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # Copyright (C) 2020 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | set -e |
| 18 | |
| 19 | # future considerations: |
| 20 | # - could we make this work with git-clang-format instead? |
| 21 | # - should we have our own formatter? |
| 22 | |
| 23 | function _aidl-format() ( |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 24 | # Find .aidl-format file to use. The file is located in one of the parent |
| 25 | # directories of the source file |
| 26 | function find-aidl-format-style() { |
| 27 | local path="$1" |
| 28 | while [[ "$path" != / ]]; |
| 29 | do |
| 30 | if find "$path" -maxdepth 1 -mindepth 1 -name .aidl-format | grep "."; then |
| 31 | return |
| 32 | fi |
| 33 | path="$(readlink -f "$path"/..)" |
| 34 | done |
| 35 | } |
| 36 | |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 37 | # Do a "reversible" conversion of the input file so that it is more friendly |
| 38 | # to clang-format. For example 'oneway interface Foo{}' is not recognized as |
| 39 | # an interface. Convert it to 'interface __aidl_oneway__ Foo{}'. |
| 40 | function prepare() { |
| 41 | # oneway interface Foo {} is not correctly recognized as an interface by |
| 42 | # clang-format. Change it to interface __aidl_oneway__ Foo {}. |
| 43 | sed -i -E 's/oneway[[:space:]]+interface/interface\ __aidl_oneway__/g' "$1" |
| 44 | |
| 45 | # When a declaration becomes too long, clang-format splits the declaration |
| 46 | # into multiple lines. In doing so, annotations that are at the front of |
| 47 | # the declaration are always split. i.e. |
| 48 | # |
| 49 | # @utf8InCpp @nullable void foo(int looooooo....ong, int looo....ong); |
| 50 | # |
| 51 | # becomes |
| 52 | # |
| 53 | # @utf8InCpp |
| 54 | # @nullable |
| 55 | # void foo(int loooooo...ong, |
| 56 | # int looo.....ong); |
| 57 | # |
| 58 | # This isn't desirable for utf8InCpp and nullable annotations which are |
| 59 | # semantically tagged to the type, not the member (field/method). We want |
| 60 | # to have the annotations in the same line as the type that they actually |
| 61 | # annotate. i.e. |
| 62 | # |
| 63 | # @utf8InCpp @nullable void foo(int looo....ong, |
| 64 | # int looo.....ong); |
| 65 | # |
| 66 | # To do so, the annotations are temporarily replaced with tokens that are |
| 67 | # not annotations. |
| 68 | sed -i -E 's/@utf8InCpp/__aidl_utf8inCpp__/g' "$1" |
| 69 | sed -i -E 's/@nullable/__aidl_nullable__/g' "$1" |
| 70 | } |
| 71 | |
| 72 | function apply-clang-format() { |
| 73 | local input="$1" |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 74 | local style="$2" |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 75 | local temp="$(mktemp)" |
Jiyong Park | 0ce4084 | 2021-01-30 11:34:10 +0900 | [diff] [blame] | 76 | local styletext="$([ -f "$style" ] && cat "$style" | tr '\n' ',' 2> /dev/null)" |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 77 | cat "$input" | clang-format \ |
Jiyong Park | ce74598 | 2021-01-22 17:05:17 +0900 | [diff] [blame] | 78 | --style='{BasedOnStyle: Google, |
| 79 | ColumnLimit: 100, |
| 80 | IndentWidth: 4, |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 81 | ContinuationIndentWidth: 8, '"${styletext}"'}' \ |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 82 | --assume-filename=${input%.*}.java \ |
| 83 | > "$temp" |
| 84 | mv "$temp" "$input" |
| 85 | } |
| 86 | |
| 87 | # clang-format is good, but doesn't perfectly fit to our needs. Fix the |
| 88 | # minor mismatches manually. |
| 89 | function fixup() { |
| 90 | # Revert the changes done during the prepare call. Notice that the |
| 91 | # original tokens (@utf8InCpp, etc.) are shorter than the temporary tokens |
| 92 | # (__aidl_utf8InCpp, etc.). This can make the output text length shorter |
| 93 | # than the specified column limit. We can try to reduce the undesirable |
| 94 | # effect by keeping the tokens to have similar lengths, but that seems to |
| 95 | # be an overkill at this moment. We can revisit this when this becomes a |
| 96 | # real problem. |
| 97 | sed -i -E 's/interface\ __aidl_oneway__/oneway\ interface/g' "$1" |
| 98 | sed -i -E 's/__aidl_utf8inCpp__/@utf8InCpp/g' "$1" |
| 99 | sed -i -E 's/__aidl_nullable__/@nullable/g' "$1" |
| 100 | |
| 101 | # clang-format adds space around "=" in annotation parameters. e.g. |
| 102 | # @Anno(a = 100). The following awk script removes the spaces back. |
| 103 | # @Anno(a = 1, b = 2) @Anno(c = 3, d = 4) int foo = 3; becomes |
| 104 | # @Anno(a=1, b=2) @Anno(c=3, d=4) int foo = 3; |
| 105 | # [^@,=] ensures that the match doesn't cross the characters, otherwise |
| 106 | # "a = 1, b = 2" would match only once and will become "a = 1, b=2". |
| 107 | gawk -i inplace \ |
| 108 | '/@[^@]+\(.*=.*\)/ { # matches a line having @anno(param = val) \ |
| 109 | print(gensub(/([^@,=]+) = ([^@,=]+)/, "\\1=\\2", "g", $0)); \ |
| 110 | done=1;\ |
| 111 | } \ |
| 112 | {if (!done) {print($0);} done=0;}' "$1" |
| 113 | } |
| 114 | |
| 115 | function format-one() { |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 116 | local mode="$1" |
| 117 | local input="$2" |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 118 | local style="$3" |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 119 | local output="$(mktemp)" |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 120 | |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 121 | cp "$input" "$output" |
| 122 | prepare "$output" |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 123 | apply-clang-format "$output" "$style" |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 124 | fixup "$output" |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 125 | |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 126 | if [ $mode = "diff" ]; then |
Jiyong Park | 2207c6b | 2021-02-24 13:39:03 +0900 | [diff] [blame] | 127 | diff "$input" "$output" || ( |
| 128 | echo "You can try to fix this by running:" |
| 129 | echo "$0 -w <file>" |
| 130 | echo "" |
| 131 | ) |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 132 | rm "$output" |
| 133 | elif [ $mode = "write" ]; then |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 134 | if diff -q "$output" "$input" >/dev/null; then |
| 135 | rm "$output" |
| 136 | else |
| 137 | mv "$output" "$input" |
| 138 | fi |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 139 | elif [ $mode = "print" ]; then |
| 140 | cat "$output" |
| 141 | rm "$output" |
| 142 | fi |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 143 | } |
| 144 | |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 145 | function show-help-and-exit() { |
| 146 | echo "Usage: $0 [options] [path...]" |
| 147 | echo " -d: display diff instead of the formatted result" |
| 148 | echo " -w: rewrite the result back to the source file, instead of stdout" |
| 149 | echo " -h: show this help message" |
| 150 | echo " [path...]: source files. if none, input is read from stdin" |
| 151 | exit 1 |
| 152 | } |
| 153 | |
| 154 | local mode=print |
| 155 | if [ $# -gt 0 ]; then |
| 156 | case "$1" in |
| 157 | -d) mode=diff; shift;; |
| 158 | -w) mode=write; shift;; |
| 159 | -h) show-help-and-exit;; |
| 160 | -*) echo "$1" is wrong option; show-help-and-exit;; |
| 161 | esac |
| 162 | fi |
| 163 | |
| 164 | if [ $# -lt 1 ]; then |
| 165 | if [ $mode = "write" ]; then |
| 166 | echo "-w not supported when input is stdin" |
| 167 | exit 1 |
| 168 | fi |
| 169 | local input="$(mktemp)" |
| 170 | cat /dev/stdin > "$input" |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 171 | local style="$(pwd)/.aidl-format" |
| 172 | format-one $mode "$input" "$style" |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 173 | rm "$input" |
| 174 | else |
| 175 | for file in "$@" |
| 176 | do |
| 177 | if [ ! -f "$file" ]; then |
| 178 | echo "$file": no such file |
| 179 | exit 1 |
| 180 | fi |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 181 | local style="$(find-aidl-format-style $(dirname "$filename"))" |
| 182 | format-one $mode "$file" "$style" |
Jiyong Park | 0b8c0b1 | 2021-01-20 17:26:08 +0900 | [diff] [blame] | 183 | done |
| 184 | fi |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 185 | ) |
| 186 | |
Jiyong Park | af816a0 | 2021-01-22 18:25:24 +0900 | [diff] [blame] | 187 | |
Steven Moreland | 0d14f5b | 2020-12-23 22:48:23 +0000 | [diff] [blame] | 188 | _aidl-format "$@" |