1#!/bin/bash -e
2
3# Copyright 2022 Google Inc. All rights reserved.
4
5# Wrapper of clang-tidy
6
7# Use case #1: by RBE input processor to get resource dir.
8# Command:
9#   clang-tidy.sh --version -- -print-resource-dir
10# Outputs:
11#   same as clang-tidy --version -- -print-resource-dir
12
13# Use case #2: by soong generated ninja build rules.
14#   Calls clang-tidy and touch output .tidy file.
15#   Calls clang or clang++ to generate .tidy.d file.
16# Environment:
17#   CLANG_CMD: 'clang' or 'clang++'
18#   TIDY_FILE: path to output .tidy file
19#   TIDY_TIMEOUT: optional for clang-tidy
20# Arguments: # same as clang-tidy
21#   <in-file-path>         (required, input file)
22#   <clang-tidy flags>     (optional)
23#   "--"                   (required to separate arguments)
24#   <clang/clang++ flags>  (optional)
25# Outputs:
26#   .../*.tidy   (the output file)
27#   .../*.tidy.d (the dependent file is output file plus .d suffix)
28#   stdout and stderr from clang-tidy
29#   stdout and stderr from clang/clang++ if clang-tidy succeeds
30
31BIN_DIR=`dirname $0`
32for x in "$@"; do
33  if [ "$x" == "--version" ]; then
34    exec "${BIN_DIR}/clang-tidy" "$@"
35  fi
36done
37
38usage() {
39    cat <<EOF
40Environment Variable:
41  required: CLANG_CMD={clang|clang++}
42  required: TIDY_FILE=<path to the output .tidy file>
43  optional: TIDY_TIMEOUT=<seconds>
44Usage: clang-tidy.sh <file> <clang-tidy flags> -- <clang/clang++ flags>
45EOF
46    exit 1
47}
48
49INF="$1"
50if [ -z "${CLANG_CMD}" ]; then
51  echo "ERROR: CLANG_CMD environment variable must be set"
52  usage
53fi
54if [ "${CLANG_CMD}" != "clang" -a "${CLANG_CMD}" != "clang++" ]; then
55  echo "ERROR: CLANG_CMD must be 'clang' or 'clang++'"
56  usage
57fi
58if [ -z "${TIDY_FILE}" ]; then
59  echo "ERROR: TIDY_FILE environment variable must be set"
60  usage
61fi
62
63ARGS=("$@")
64NARGS=${#@}
65# call clang-tidy with all arguments
66TIDY_CLANG_FLAGS=("${ARGS[@]}")
67
68# find "--" in ARGS and set CLANG_FLAGS
69LONGDASH=""
70for idx in ${!ARGS[@]}; do
71  if [ "${ARGS[$idx]}" == "--" ]; then
72    LONGDASH=$idx
73  fi
74done
75if [ -z "${LONGDASH}" ]; then
76  echo "ERROR: missing '--' argument"
77  usage
78fi
79# call clang with clang flags, after INF
80N=$(expr $LONGDASH + 1)
81CLANG_FLAGS=("${ARGS[@]:$N:$NARGS}")
82# add more flags to generate .tidy.d file
83CLANG_FLAGS+=("-E" "-o" "/dev/null" "-MQ" "${TIDY_FILE}" "-MD" "-MF" "${TIDY_FILE}.d")
84
85TIDY_STDOUT="${TIDY_FILE}.stdout"
86TIDY_STDERR="${TIDY_FILE}.stderr"
87CLANG_STDOUT="${TIDY_FILE}.d.stdout"
88CLANG_STDERR="${TIDY_FILE}.d.stderr"
89
90OUT_DIR=`dirname ${TIDY_FILE}`
91mkdir -p ${OUT_DIR}
92
93call_clang() {
94  "${BIN_DIR}/${CLANG_CMD}" "${INF}" "${CLANG_FLAGS[@]}" \
95    > ${CLANG_STDOUT} 2> ${CLANG_STDERR} & \
96  CLANG_PID=$!
97}
98
99call_clang_tidy() {
100  "${BIN_DIR}/clang-tidy" "${TIDY_CLANG_FLAGS[@]}" \
101    > ${TIDY_STDOUT} 2> ${TIDY_STDERR} & \
102  TIDY_PID=$!
103}
104
105rm -f ${TIDY_FILE} ${TIDY_STDOUT} ${TIDY_STDERR} ${CLANG_STDOUT} ${CLANG_STDERR}
106call_clang
107call_clang_tidy
108
109CLANG_RESULT=0
110TIDY_RESULT=0
111wait ${CLANG_PID} || CLANG_RESULT=$?
112if [ "${CLANG_RESULT}" == 0 ]; then
113  # RBE put some files in /b/f/w/, which should be removed in .d files.
114  # Or ninja dependency check will not find them and run this script again.
115  sed -i -e 's:/b/f/w/::' ${TIDY_FILE}.d
116fi
117wait ${TIDY_PID} || TIDY_RESULT=$?
118
119cat ${TIDY_STDOUT}
120cat ${TIDY_STDERR} 1>&2
121if [ "${TIDY_RESULT}" == 0 ]; then
122  touch ${TIDY_FILE}
123  # dump clang/clang++ output and use its exit code
124  cat ${CLANG_STDOUT}
125  cat ${CLANG_STDERR} 1>&2
126  TIDY_RESULT=${CLANG_RESULT}
127else
128  # ignore clang/clang++ output
129  rm -f ${TIDY_FILE}.d
130fi
131
132rm -f ${TIDY_STDOUT} ${TIDY_STDERR} ${CLANG_STDOUT} ${CLANG_STDERR}
133exit ${TIDY_RESULT}
134