blob: 1030ccaaba1d97a50d03048eddf5fa46c4db8bf7 [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() (
Steven Moreland0d14f5b2020-12-23 22:48:23 +000024 # Do a "reversible" conversion of the input file so that it is more friendly
25 # to clang-format. For example 'oneway interface Foo{}' is not recognized as
26 # an interface. Convert it to 'interface __aidl_oneway__ Foo{}'.
27 function prepare() {
28 # oneway interface Foo {} is not correctly recognized as an interface by
29 # clang-format. Change it to interface __aidl_oneway__ Foo {}.
30 sed -i -E 's/oneway[[:space:]]+interface/interface\ __aidl_oneway__/g' "$1"
31
32 # When a declaration becomes too long, clang-format splits the declaration
33 # into multiple lines. In doing so, annotations that are at the front of
34 # the declaration are always split. i.e.
35 #
36 # @utf8InCpp @nullable void foo(int looooooo....ong, int looo....ong);
37 #
38 # becomes
39 #
40 # @utf8InCpp
41 # @nullable
42 # void foo(int loooooo...ong,
43 # int looo.....ong);
44 #
45 # This isn't desirable for utf8InCpp and nullable annotations which are
46 # semantically tagged to the type, not the member (field/method). We want
47 # to have the annotations in the same line as the type that they actually
48 # annotate. i.e.
49 #
50 # @utf8InCpp @nullable void foo(int looo....ong,
51 # int looo.....ong);
52 #
53 # To do so, the annotations are temporarily replaced with tokens that are
54 # not annotations.
55 sed -i -E 's/@utf8InCpp/__aidl_utf8inCpp__/g' "$1"
56 sed -i -E 's/@nullable/__aidl_nullable__/g' "$1"
57 }
58
59 function apply-clang-format() {
60 local input="$1"
61 local temp="$(mktemp)"
62 cat "$input" | clang-format \
Jiyong Parkce745982021-01-22 17:05:17 +090063 --style='{BasedOnStyle: Google,
64 ColumnLimit: 100,
65 IndentWidth: 4,
66 ContinuationIndentWidth: 8}' \
Steven Moreland0d14f5b2020-12-23 22:48:23 +000067 --assume-filename=${input%.*}.java \
68 > "$temp"
69 mv "$temp" "$input"
70 }
71
72 # clang-format is good, but doesn't perfectly fit to our needs. Fix the
73 # minor mismatches manually.
74 function fixup() {
75 # Revert the changes done during the prepare call. Notice that the
76 # original tokens (@utf8InCpp, etc.) are shorter than the temporary tokens
77 # (__aidl_utf8InCpp, etc.). This can make the output text length shorter
78 # than the specified column limit. We can try to reduce the undesirable
79 # effect by keeping the tokens to have similar lengths, but that seems to
80 # be an overkill at this moment. We can revisit this when this becomes a
81 # real problem.
82 sed -i -E 's/interface\ __aidl_oneway__/oneway\ interface/g' "$1"
83 sed -i -E 's/__aidl_utf8inCpp__/@utf8InCpp/g' "$1"
84 sed -i -E 's/__aidl_nullable__/@nullable/g' "$1"
85
86 # clang-format adds space around "=" in annotation parameters. e.g.
87 # @Anno(a = 100). The following awk script removes the spaces back.
88 # @Anno(a = 1, b = 2) @Anno(c = 3, d = 4) int foo = 3; becomes
89 # @Anno(a=1, b=2) @Anno(c=3, d=4) int foo = 3;
90 # [^@,=] ensures that the match doesn't cross the characters, otherwise
91 # "a = 1, b = 2" would match only once and will become "a = 1, b=2".
92 gawk -i inplace \
93 '/@[^@]+\(.*=.*\)/ { # matches a line having @anno(param = val) \
94 print(gensub(/([^@,=]+) = ([^@,=]+)/, "\\1=\\2", "g", $0)); \
95 done=1;\
96 } \
97 {if (!done) {print($0);} done=0;}' "$1"
98 }
99
100 function format-one() {
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900101 local mode="$1"
102 local input="$2"
103 local output="$(mktemp)"
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000104
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900105 cp "$input" "$output"
106 prepare "$output"
107 apply-clang-format "$output"
108 fixup "$output"
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000109
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900110 if [ $mode = "diff" ]; then
111 diff "$input" "$output"
112 rm "$output"
113 elif [ $mode = "write" ]; then
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000114 if diff -q "$output" "$input" >/dev/null; then
115 rm "$output"
116 else
117 mv "$output" "$input"
118 fi
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900119 elif [ $mode = "print" ]; then
120 cat "$output"
121 rm "$output"
122 fi
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000123 }
124
Jiyong Park0b8c0b12021-01-20 17:26:08 +0900125 function show-help-and-exit() {
126 echo "Usage: $0 [options] [path...]"
127 echo " -d: display diff instead of the formatted result"
128 echo " -w: rewrite the result back to the source file, instead of stdout"
129 echo " -h: show this help message"
130 echo " [path...]: source files. if none, input is read from stdin"
131 exit 1
132 }
133
134 local mode=print
135 if [ $# -gt 0 ]; then
136 case "$1" in
137 -d) mode=diff; shift;;
138 -w) mode=write; shift;;
139 -h) show-help-and-exit;;
140 -*) echo "$1" is wrong option; show-help-and-exit;;
141 esac
142 fi
143
144 if [ $# -lt 1 ]; then
145 if [ $mode = "write" ]; then
146 echo "-w not supported when input is stdin"
147 exit 1
148 fi
149 local input="$(mktemp)"
150 cat /dev/stdin > "$input"
151 format-one $mode "$input"
152 rm "$input"
153 else
154 for file in "$@"
155 do
156 if [ ! -f "$file" ]; then
157 echo "$file": no such file
158 exit 1
159 fi
160 format-one $mode "$file"
161 done
162 fi
Steven Moreland0d14f5b2020-12-23 22:48:23 +0000163)
164
165_aidl-format "$@"