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() ( |
| 24 | if [ $# != 1 ]; then |
| 25 | echo "Usage: $0 <file/directory>" |
| 26 | exit 1 |
| 27 | fi |
| 28 | |
| 29 | local loc="$1" |
| 30 | |
| 31 | local tmpfile="$tmpfile" |
| 32 | |
| 33 | # Do a "reversible" conversion of the input file so that it is more friendly |
| 34 | # to clang-format. For example 'oneway interface Foo{}' is not recognized as |
| 35 | # an interface. Convert it to 'interface __aidl_oneway__ Foo{}'. |
| 36 | function prepare() { |
| 37 | # oneway interface Foo {} is not correctly recognized as an interface by |
| 38 | # clang-format. Change it to interface __aidl_oneway__ Foo {}. |
| 39 | sed -i -E 's/oneway[[:space:]]+interface/interface\ __aidl_oneway__/g' "$1" |
| 40 | |
| 41 | # When a declaration becomes too long, clang-format splits the declaration |
| 42 | # into multiple lines. In doing so, annotations that are at the front of |
| 43 | # the declaration are always split. i.e. |
| 44 | # |
| 45 | # @utf8InCpp @nullable void foo(int looooooo....ong, int looo....ong); |
| 46 | # |
| 47 | # becomes |
| 48 | # |
| 49 | # @utf8InCpp |
| 50 | # @nullable |
| 51 | # void foo(int loooooo...ong, |
| 52 | # int looo.....ong); |
| 53 | # |
| 54 | # This isn't desirable for utf8InCpp and nullable annotations which are |
| 55 | # semantically tagged to the type, not the member (field/method). We want |
| 56 | # to have the annotations in the same line as the type that they actually |
| 57 | # annotate. i.e. |
| 58 | # |
| 59 | # @utf8InCpp @nullable void foo(int looo....ong, |
| 60 | # int looo.....ong); |
| 61 | # |
| 62 | # To do so, the annotations are temporarily replaced with tokens that are |
| 63 | # not annotations. |
| 64 | sed -i -E 's/@utf8InCpp/__aidl_utf8inCpp__/g' "$1" |
| 65 | sed -i -E 's/@nullable/__aidl_nullable__/g' "$1" |
| 66 | } |
| 67 | |
| 68 | function apply-clang-format() { |
| 69 | local input="$1" |
| 70 | local temp="$(mktemp)" |
| 71 | cat "$input" | clang-format \ |
| 72 | --style='{BasedOnStyle: Google, BreakAfterJavaFieldAnnotations: false, ColumnLimit: 100}' \ |
| 73 | --assume-filename=${input%.*}.java \ |
| 74 | > "$temp" |
| 75 | mv "$temp" "$input" |
| 76 | } |
| 77 | |
| 78 | # clang-format is good, but doesn't perfectly fit to our needs. Fix the |
| 79 | # minor mismatches manually. |
| 80 | function fixup() { |
| 81 | # Revert the changes done during the prepare call. Notice that the |
| 82 | # original tokens (@utf8InCpp, etc.) are shorter than the temporary tokens |
| 83 | # (__aidl_utf8InCpp, etc.). This can make the output text length shorter |
| 84 | # than the specified column limit. We can try to reduce the undesirable |
| 85 | # effect by keeping the tokens to have similar lengths, but that seems to |
| 86 | # be an overkill at this moment. We can revisit this when this becomes a |
| 87 | # real problem. |
| 88 | sed -i -E 's/interface\ __aidl_oneway__/oneway\ interface/g' "$1" |
| 89 | sed -i -E 's/__aidl_utf8inCpp__/@utf8InCpp/g' "$1" |
| 90 | sed -i -E 's/__aidl_nullable__/@nullable/g' "$1" |
| 91 | |
| 92 | # clang-format adds space around "=" in annotation parameters. e.g. |
| 93 | # @Anno(a = 100). The following awk script removes the spaces back. |
| 94 | # @Anno(a = 1, b = 2) @Anno(c = 3, d = 4) int foo = 3; becomes |
| 95 | # @Anno(a=1, b=2) @Anno(c=3, d=4) int foo = 3; |
| 96 | # [^@,=] ensures that the match doesn't cross the characters, otherwise |
| 97 | # "a = 1, b = 2" would match only once and will become "a = 1, b=2". |
| 98 | gawk -i inplace \ |
| 99 | '/@[^@]+\(.*=.*\)/ { # matches a line having @anno(param = val) \ |
| 100 | print(gensub(/([^@,=]+) = ([^@,=]+)/, "\\1=\\2", "g", $0)); \ |
| 101 | done=1;\ |
| 102 | } \ |
| 103 | {if (!done) {print($0);} done=0;}' "$1" |
| 104 | } |
| 105 | |
| 106 | function format-one() { |
| 107 | local input="$1" |
| 108 | local output="$(mktemp)" |
| 109 | |
| 110 | cp "$input" "$output" |
| 111 | prepare "$output" |
| 112 | apply-clang-format "$output" |
| 113 | fixup "$output" |
| 114 | |
| 115 | if diff -q "$output" "$input" >/dev/null; then |
| 116 | rm "$output" |
| 117 | else |
| 118 | mv "$output" "$input" |
| 119 | fi |
| 120 | } |
| 121 | |
| 122 | while read -d '' aidl; do |
| 123 | format-one "$aidl" |
| 124 | done < <(find "$loc" -not -path "*/aidl_api/*" -type f -name '*.aidl' -print0) |
| 125 | ) |
| 126 | |
| 127 | _aidl-format "$@" |