HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / tools / coveralls-tool
1 #!/bin/bash
2
3 # This script collates gcov data after one has configured with --enable-gcov,
4 # built, and run tests.  It either outputs or POSTs to Coveralls a JSON text in
5 # the schema for the Coveralls API, which is documented here:
6 #
7 # https://docs.coveralls.io/api-introduction
8 # https://docs.coveralls.io/api-reference
9 #
10 # Currently only files in source languages supported by gcov(1) are reported
11 # on, though this can easily be extended.  Currently that's only C/C++ files.
12 #
13 # This script is specifically written for Heimdal, which is an open source C
14 # codebases that uses autoconf and libtool for its build system.  This means
15 # that sometimes the gcov notes and data files are not necessarily where the
16 # gcov(1) utility would find them, which is why this script exists instead of
17 # using some other integration script.
18 #
19 # Although this is specific to Heimdal, it can be extended.
20 #
21 # Note that one side effect of running this script, gcov(1) will be run for all
22 # C/C++ source files in the workspace.  As well, some gcov notes and data files
23 # maybe hard-linked to other names.  However, this script should be idempotent.
24
25 set -euo pipefail
26 set +o noglob
27
28 PROG=${0##*/}
29
30 job=${TRAVIS_JOB_ID:-}
31 out=
32 post=false
33 repo=
34 flst=
35 quiet=false
36 branch=
37 srcdir=$PWD
38 objdir=
39 token=${COVERALLS_REPO_TOKEN:-}
40 origin=
41 verbose=0
42
43 function usage {
44     ((${1:-1})) && exec 1>&2
45     cat <<EOF
46 Usage: $PROG [OPTIONS]
47     Options:
48
49      -q         Quiet.  Do not even emit warnings.
50      -v         Verbose (on stderr).  May be given multiple times.
51      -o -       Output to stdout instead of POSTing to Coveralls.
52      -o FILE    Output to FILE   instead of POSTing to Coveralls.
53      -s CI-NAME Name of CI (e.g., "travis-ci")
54                 Defaults to travis-ci.
55      -t TOKEN   Token for Coveralls.
56                 Defaults to \$COVERALLS_REPO_TOKEN.
57      -b BRANCH  Name of branch the report is for.
58                 Defaults to \$TRAVIS_BRANCH or currently-checked out branch in
59                 SRCDIR.
60      -J ID      Job ID (e.g., Travis-CI job ID)
61                 Defaults to \${TRAVIS_JOB_ID}.
62      -i FILE    Lists source files to run gcov(1) against
63                 Defaults to \<(git ls-files -- '*.c' '*.cpp').
64      -S SRCDIR  Path to workspace
65                 Defaults to \${PWD}.
66      -O OBJDIR  Path to object directory if workspace is built out of tree
67                 Defaults to SRCDIR.
68      -U ORIGIN  Name of origin.
69                 Defaults to tracked upstream remote of BRANCH.
70      -R URI     Repository URI
71                 Defaults to git@github.com:\${TRAVIS_REPO_SLUG} or the push URI
72                 for the ORIGIN remote of the workspace at SRCDIR.
73
74     $PROG will look for .gcno and .gcda files in OBJDIR for source files
75     in the workspace at SRCDIR and will run gcov on them, and produce
76     a request body as JSON in FILE (or stdout if -o FILE not given)
77     for the Coveralls API.
78
79     If -o FILE is not given, then $PROG will POST the JSON to Coveralls.
80     If -o FILE is given, then $PROG will not POST it to Coveralls.
81
82     If SRCDIR == OBJDIR == \$PWD, then -S and -O need not be given.
83     If running in a Travis-CI build, -J, -R, and -b need not be given, and -t
84     should not be given -- instead you should set a secret COVERALLS_REPO_TOKEN
85     environment variable in your project's Travis-CI's settings.
86
87     Only C and C++ source files are reported on.  E.g., Yacc/Bison/Flex
88     source files are not reported.
89
90     The resulting JSON output is or can be POSTed to Coveralls with:
91
92       $ curl -sfg -X POST -F "json_file=@\${FILE}" -F "Filename=json_file" \\
93              https://coveralls.io/api/v1/jobs
94 EOF
95     exit ${1:-1}
96 }
97
98 while getopts +:J:O:R:S:U:b:hi:o:qs:t:vx opt; do
99 case "$opt" in
100 J) job=$OPTARG;;
101 O) cd "$OPTARG"; objdir=$PWD; cd "$OLDPWD";;
102 R) repo=$OPTARG;;
103 S) cd "$OPTARG"; srcdir=$PWD; cd "$OLDPWD";;
104 U) origin=$OPTARG;;
105 b) branch=;;
106 h) usage 0;;
107 i) flst=$OPTARG;;
108 o) out=$OPTARG;;
109 q) quiet=true; verbose=0;;
110 s) ci=$OPTARG;;
111 t) token=$OPTARG;;
112 v) quiet=false; ((verbose++)) || true; ((verbose > 3)) && set -vx;;
113 *) usage 1;;
114 esac
115 done
116
117 # Note: we don't cd to $srcdir or $objdir or anywhere, so if $out is a relative
118 # path, we do the right thing.
119
120 : ${objdir:=${srcdir}}
121 : ${branch:=${TRAVIS_BRANCH:-$(cd "$srcdir" && git rev-parse --abbrev-ref HEAD)}}
122
123 if [[ -z ${origin:-} ]]; then
124     origin=$(
125         git for-each-ref \
126             --format="%(refname:short) %(upstream:remotename)" refs/heads |
127             while read gb gr; do
128                 [[ $gb = $branch ]] || continue
129                 printf '%s\n' "$gr"
130                 break
131             done
132     )
133 fi
134
135 if [[ -z ${repo:-} ]]; then
136     if [[ -n ${TRAVIS_REPO_SLUG:-} ]]; then
137         repo=git@github.com:${TRAVIS_REPO_SLUG:-heimdal/heimdal}
138     else
139         repo=$(cd "$srcdir" && git remote get-url --push "$origin")
140     fi
141 fi
142
143 if ((verbose > 1)); then
144     exec 3>&2
145 else
146     exec 3>/dev/null
147 fi
148
149 d=
150 function cleanup {
151     [[ -n $d ]] && rm -rf "$d"
152 }
153
154 trap cleanup EXIT
155 d=$(mktemp -d)
156 touch "${d}/f"
157
158 declare -a gcov
159
160 (cd "$srcdir" &&
161  if [[ -n $flst ]]; then cat "$flst"; else git ls-files -- '*.c' '*.cpp'; fi) |
162 while read f; do
163     # Remember to be careful to refer to ${srcdir}/${f}
164     ((verbose)) && printf 'Processing: %s\n' "$f" 1>&2
165
166     dir=${f%/*}
167     base=${f##*/}
168     base=${base%.*}
169
170     if [[ ! -f ${objdir}/${dir}/.libs/${base}.gcda && ! -f ${objdir}/${dir}/${base}.gcda ]]; then
171         # Look for .libs/libfoo_la-${base}.gcda -- we don't know "foo", and
172         # there may be more than one!
173         gcda=
174         for gcda in ${objdir}/${dir}/.libs/*_la-${base}.gcda; do
175             break
176         done
177         gcno=
178         for gcno in ${objdir}/${dir}/.libs/*_la-${base}.gcno; do
179             break
180         done
181         [[ -n $gcno && -f $gcno ]] && ln -f "$gcno" "${objdir}/${dir}/.libs/${base}.gcno"
182         [[ -n $gcda && -f $gcda ]] && ln -f "$gcda" "${objdir}/${dir}/.libs/${base}.gcda"
183         if [[ ( -n $gcda && ! -f $gcda ) || ( -n $gcno && ! -f $gcno ) ]]; then
184             $quiet || printf 'Warning: %s has no gcov notes file\n' "$f" 1>&2
185             continue
186         fi
187     fi
188
189     if [[ -f ${objdir}/${dir}/.libs/${base}.gcda ]]; then
190         ((verbose > 1)) && printf 'Running gcov for %s using gcda from .libs\n' "$f" 1>&2
191         if ! (cd "${objdir}/${f%/*}"; ((verbose > 2)) && set -vx; gcov -o .libs "${f##*/}") 1>&3; then
192             $quiet || printf 'Warning: gcov failed for %s\n' "$f" 1>&2
193             continue
194         fi
195     elif [[ -f ${objdir}/${dir}/${base}.gcda ]]; then
196         if ! (cd "${objdir}/${f%/*}"; ((verbose > 2)) && set -vx; gcov "${f##*/}") 1>&3; then
197             $quiet || printf 'Warning: gcov failed for %s\n' "$f" 1>&2
198             continue
199         fi
200     fi
201
202     if [[ ! -f ${objdir}/${f}.gcov ]]; then
203         $quiet || printf 'Warning: gcov did not produce a .gcov file for %s\n' "$f" 1>&2
204         continue
205     fi
206
207     md5=$(md5sum "${srcdir}/${f}")
208     md5=${md5%% *}
209
210     jq -Rn --arg sum "${md5}" --arg f "$f" '
211         {
212             name: $f,
213             source_digest: $sum,
214             coverage: [
215                 inputs
216               | split(":")
217               | (.[1] |= tonumber)
218               | select(.[1] > 0)
219               | if .[0]|endswith("#")
220                 then 0
221                 elif .[0]|endswith("-")
222                 then null
223                 else .[0]|tonumber
224                 end
225             ]
226         }
227     ' "${objdir}/${f}.gcov" >> "${d}/f"
228 done
229
230 function make_report {
231     jq -s --arg job "$job" \
232           --arg ci "${ci:-travis-ci}" \
233           --arg token "$token" \
234           --arg repo "$repo" \
235           --arg branch "$branch" \
236           --arg upstream "$origin" \
237           --arg head "$(git log -n1 --format=%H)" \
238           --arg subject "$(git log -n1 --format=%s)" \
239           --arg aN "$(git log -n1 --format=%aN)" \
240           --arg ae "$(git log -n1 --format=%ae)" \
241           --arg cN "$(git log -n1 --format=%cN)" \
242           --arg ce "$(git log -n1 --format=%ce)" \
243         '{
244             service_job_id: $job,
245             service_name: $ci,
246             repo_token: $token,
247             git: {
248                 id: $head,
249                 author_name:  $aN,
250                 author_email: $ae,
251                 committer_name:  $cN,
252                 committer_email: $ce,
253                 message: $subject,
254                 branch: $branch,
255                 remotes: [ {
256                     "name": $upstream,
257                     "url": $repo
258                     }
259                 ]
260             },
261             source_files: .
262         }' "${d}/f"
263 }
264
265 if [[ -z $out ]]; then
266     post=true
267     make_report > "${d}/out"
268 elif [[ $out = - ]]; then
269     make_report
270 else
271     make_report > "${out}"
272 fi
273
274 if $post && [[ $out != /dev/stdout ]]; then
275     curl -sfg -X POST -F "json_file=@${d}/out" -F "Filename=json_file" \
276          https://coveralls.io/api/v1/jobs
277 fi