blob: 4f2a10486aff0dc23d671167bde7adb1a8cb5e64 [file] [log] [blame]
Steven Moreland0d14f5b2020-12-23 22:48:23 +00001#!/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
17set -e
18
19# future considerations:
20# - could we make this work with git-clang-format instead?
21# - should we have our own formatter?
22
23function _aidl-format() (
Jiyong Parkaf816a02021-01-22 18:25:24 +090024 # 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 Moreland0d14f5b2020-12-23 22:48:23 +000037 # 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 Parkaf816a02021-01-22 18:25:24 +090074 local style="$2"
Steven Moreland0d14f5b2020-12-23 22:48:23 +000075 local temp="$(mktemp)"
Jiyong Park0ce40842021-01-30 11:34:10 +090076 local styletext="$([ -f "$style" ] && cat "$style" | tr '\n' ',' 2> /dev/null)"
Steven Moreland0d14f5b2020-12-23 22:48:23 +000077 cat "$input" | clang-format \
Jiyong Parkce745982021-01-22 17:05:17 +090078 --style='{BasedOnStyle: Google,
79 ColumnLimit: 100,
80 IndentWidth: 4,
Jiyong Parkaf816a02021-01-22 18:25:24 +090081 ContinuationIndentWidth: 8, '"${styletext}"'}' \
Steven Moreland0d14f5b2020-12-23 22:48:23 +000082 --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 Park0b8c0b12021-01-20 17:26:08 +0900116 local mode="$1"
117 local input="$2"
Jiyong Parkaf816a02021-01-22 18:25:24 +0900118 local style="$3"
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900119 local output="$(mktemp)"
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000120
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900121 cp "$input" "$output"
122 prepare "$output"
Jiyong Parkaf816a02021-01-22 18:25:24 +0900123 apply-clang-format "$output" "$style"
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900124 fixup "$output"
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000125
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900126 if [ $mode = "diff" ]; then
Jiyong Park2207c6b2021-02-24 13:39:03 +0900127 diff "$input" "$output" || (
128 echo "You can try to fix this by running:"
129 echo "$0 -w <file>"
130 echo ""
131 )
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900132 rm "$output"
133 elif [ $mode = "write" ]; then
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000134 if diff -q "$output" "$input" >/dev/null; then
135 rm "$output"
136 else
137 mv "$output" "$input"
138 fi
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900139 elif [ $mode = "print" ]; then
140 cat "$output"
141 rm "$output"
142 fi
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000143 }
144
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900145 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 Parkaf816a02021-01-22 18:25:24 +0900171 local style="$(pwd)/.aidl-format"
172 format-one $mode "$input" "$style"
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900173 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 Parkaf816a02021-01-22 18:25:24 +0900181 local style="$(find-aidl-format-style $(dirname "$filename"))"
182 format-one $mode "$file" "$style"
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900183 done
184 fi
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000185)
186
Jiyong Parkaf816a02021-01-22 18:25:24 +0900187
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000188_aidl-format "$@"