1#!/bin/bash
2
3#
4#    Copyright 2011-2016 Nest Labs Inc. All Rights Reserved.
5#
6#    Licensed under the Apache License, Version 2.0 (the "License");
7#    you may not use this file except in compliance with the License.
8#    You may obtain a copy of the License at
9#
10#    http://www.apache.org/licenses/LICENSE-2.0
11#
12#    Unless required by applicable law or agreed to in writing, software
13#    distributed under the License is distributed on an "AS IS" BASIS,
14#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#    See the License for the specific language governing permissions and
16#    limitations under the License.
17#
18
19#
20#    Description:
21#      This file attempts to find and generate a package version
22#      including, if necessary, the number of commits from the last
23#      GIT tag and the current GIT hash corresponding to HEAD for the
24#      current branch.
25#
26#      This is largely cobbled together from similar scripts in other
27#      packages that are maintained in GIT (linux, u-boot, parted, etc.).
28#
29#      This can produce version information such as:
30#
31#        1.0.1
32#        1.0.1-dirty
33#        1.0.1-00032-gab50dbb
34#        1.0.1-00032-gab50dbb-dirty
35#
36
37# Constants
38
39ROOTDIR=${PREFIX}/
40
41BINDIR=${ROOTDIR}bin
42DATADIR=${ROOTDIR}share
43DEVICEDIR=${ROOTDIR}dev
44CONFDIR=${ROOTDIR}etc
45LIBDIR=${ROOTDIR}lib
46LIBEXECDIR=${ROOTDIR}libexec
47VARDIR=${ROOTDIR}var
48LOGDIR=${VARDIR}/log
49MANDIR=${ROOTDIR}man
50SBINDIR=${ROOTDIR}sbin
51
52USRDIR=${ROOTDIR}usr
53USRBINDIR=${USRDIR}/bin
54USRDATADIR=${USRDIR}/share
55USRLIBDIR=${USRDIR}/lib
56USRLIBEXECDIR=${USRDIR}/libexec
57USRSBINDIR=${USRDIR}/sbin
58
59AWK=${USRBINDIR}/awk
60BASENAME=${USRBINDIR}/basename
61CAT=${BINDIR}/cat
62ECHO="${BINDIR}/echo"
63NULL=${DEVICEDIR}/null
64PRINTF=${USRBINDIR}/printf
65RM=${BINDIR}/rm
66SED=${BINDIR}/sed
67
68VERSION=""
69
70#
71# usage <status>
72#
73# Description:
74#   This routine prints out the proper command line usage for this
75#   program and then exits with the specified status.
76#
77# Input(s):
78#   status - Exit status to exit the program with.
79#
80# Returns:
81#   This subroutine does not return.
82#
83usage() {
84	local name=`${BASENAME} ${0}`
85
86	${ECHO} "Usage: ${name} [options] [ <project root> ]"
87
88	if [ ${1} -ne 0 ]; then
89		${ECHO} "Try '${name} -h' for more information."
90	fi
91
92	if [ ${1} -ne 1 ]; then
93${CAT} << EOF
94  -b, --build-version=VERSION  Specify VERSION as the build version to generate
95                               extra build information against.
96  -h, --help                   Print this help, then exit.
97EOF
98	fi
99
100	exit ${1}
101}
102
103#
104# gitversion <string> <directory> <version>
105#
106# Description:
107#   This routine prints out any GIT version information appended to the
108#   end of the package version, including the number of commits from
109#   the last GIT tag and the current GIT hash corresponding to HEAD
110#   for the current branch.
111#
112# Input(s):
113#   string    - The current version string which may be empty.
114#   directory - The current directory.
115#   version   - The optional current package version.
116#
117# Returns:
118#   N/A
119#
120gitversion() {
121	local string="${1}"
122	local dir="${2}"
123	local version="${3}"
124	local head
125	local exact
126	local dtag
127	local gitversion
128
129	# Retrieve the shortened, unique GIT hash associated with the
130	# 'HEAD' GIT object
131
132	head=`test -d .git && git rev-parse --verify --short HEAD 2> ${NULL}`
133
134	# If we found a hash, we are actually in a GIT repository; continue.
135
136	if [ -n "${head}" ]; then
137	    	# Check to see if we have a position in GIT that is
138	    	# exactly at an existing tag (e.g. 1.0.2). If we are,
139	    	# just use it and add a dirty qualifier. Otherwise,
140	    	# work through the logic to determine how far off the
141	    	# tag the tree is.
142
143	    	exact="`git describe --exact-match 2> ${NULL}`"
144
145		if [ -z "${exact}" ] || [ -n "${version}" ] && [ "${version}" != "${exact}" ]; then
146			dtag="`git describe 2> ${NULL}`"
147
148			# If we are n commits away from a tag, then
149			# print n and a shortened version of the
150			# hash. Otherwise, just print the hash.
151			#
152			# If we are at an exact version, then there
153			# won't be a delta or a hash, just use the
154			# exact tag.
155
156			if [ -n "${dtag}" ]; then
157				if [ "${dtag}" == "${exact}" ]; then
158					gitversion="${dtag}"
159				else
160					gitversion=`${PRINTF} "${dtag}" | ${AWK} -F '-' '{printf("%s-%05d-%s", $(NF-2),$(NF-1),$(NF))}' 2> ${NULL}`
161				fi
162
163			else
164				gitversion=`${PRINTF} "g${head}"`
165
166			fi
167
168                else
169			gitversion="${exact}"
170
171		fi
172
173		# Update the index if we are in a writable directory
174		# so that we can successfully check for a dirty (has
175		# uncommitted changes or unresolved merges) tree.
176
177		if [ -w "${dir}" ]; then
178			git update-index --refresh --unmerged > ${NULL}
179		fi
180
181		# Now check for such a dirty tree and add to the "string"
182		# if we found one.
183
184		if git diff-index --name-only HEAD | read dummy; then
185		    	if [ -n "${gitversion}" ]; then
186				gitversion="${gitversion}-dirty"
187			else
188				gitversion="dirty"
189			fi
190    		fi
191
192	else
193		gitversion="${version}"
194
195	fi
196
197	if [ -n "${string}" ] && [ -n "${gitversion}" ]; then
198		string="${string}-${gitversion}"
199	else
200		string="${gitversion}"
201	fi
202
203	${PRINTF} "${string}"
204}
205
206#
207# Main Program Body
208#
209
210while [ ${#} -gt 0 ]; do
211    	if [ ${1:0:1} == "-" ]; then
212		if [ "${1}" == "-h" ] || [ "${1}" == "--help" ]; then
213			usage 0
214
215		elif [ "${1}" == "-b" ] || [ "${1}" == "--build-version" ]; then
216			version="${2}"
217			shift 2
218
219		else
220		    	${ECHO} "Unknown argument '${1}'."
221			usage 1
222
223		fi
224
225	else
226		break
227
228	fi
229done
230
231if [ ${#} -gt 1 ]; then
232	usage 1
233elif [ ${#} -eq 1 ]; then
234	tree="${1}"
235else
236	tree="."
237fi
238
239if [ "${tree}" != "." ]; then
240	cd "${tree}"
241fi
242
243VERSION="`gitversion \"${VERSION}\" . ${version}`"
244
245${PRINTF} "${VERSION}"
246