kbuild: Clean up and speed up the localversion logic

Now that we run scripts/setlocalversion during every build, it makes
sense to move all the localversion logic there. This cleans up the
toplevel Makefile and also makes sure that the script is called only
once in 'make prepare' (previously, it would be called every time due to
a variable expansion in an ifneq statement). No user-visible change is
intended, unless one runs the setlocalversion script directly.

Reported-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Nico Schottelius <nico-linuxsetlocalversion@schottelius.org>
Signed-off-by: Michal Marek <mmarek@suse.cz>
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 46989b8..d6a866e 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -10,73 +10,158 @@
 #
 
 usage() {
-	echo "Usage: $0 [srctree]" >&2
+	echo "Usage: $0 [--scm-only] [srctree]" >&2
 	exit 1
 }
 
-cd "${1:-.}" || usage
+scm_only=false
+srctree=.
+if test "$1" = "--scm-only"; then
+	scm_only=true
+	shift
+fi
+if test $# -gt 0; then
+	srctree=$1
+	shift
+fi
+if test $# -gt 0 -o ! -d "$srctree"; then
+	usage
+fi
 
-# Check for git and a git repo.
-if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
+scm_version()
+{
+	local short=false
 
-	# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore it,
-	# because this version is defined in the top level Makefile.
-	if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
+	cd "$srctree"
+	if test -e .scmversion; then
+		cat "$_"
+		return
+	fi
+	if test "$1" = "--short"; then
+		short=true
+	fi
 
-		# If we are past a tagged commit (like "v2.6.30-rc5-302-g72357d5"),
-		# we pretty print it.
-		if atag="`git describe 2>/dev/null`"; then
-			echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
+	# Check for git and a git repo.
+	if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
 
-		# If we don't have a tag at all we print -g{commitish}.
-		else
-			printf '%s%s' -g $head
+		# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore
+		# it, because this version is defined in the top level Makefile.
+		if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
+
+			# If only the short version is requested, don't bother
+			# running further git commands
+			if $short; then
+				echo "+"
+				return
+			fi
+			# If we are past a tagged commit (like
+			# "v2.6.30-rc5-302-g72357d5"), we pretty print it.
+			if atag="`git describe 2>/dev/null`"; then
+				echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
+
+			# If we don't have a tag at all we print -g{commitish}.
+			else
+				printf '%s%s' -g $head
+			fi
 		fi
+
+		# Is this git on svn?
+		if git config --get svn-remote.svn.url >/dev/null; then
+			printf -- '-svn%s' "`git svn find-rev $head`"
+		fi
+
+		# Update index only on r/w media
+		[ -w . ] && git update-index --refresh --unmerged > /dev/null
+
+		# Check for uncommitted changes
+		if git diff-index --name-only HEAD | grep -v "^scripts/package" \
+		    | read dummy; then
+			printf '%s' -dirty
+		fi
+
+		# All done with git
+		return
 	fi
 
-	# Is this git on svn?
-	if git config --get svn-remote.svn.url >/dev/null; then
-	        printf -- '-svn%s' "`git svn find-rev $head`"
+	# Check for mercurial and a mercurial repo.
+	if hgid=`hg id 2>/dev/null`; then
+		tag=`printf '%s' "$hgid" | cut -d' ' -f2`
+
+		# Do we have an untagged version?
+		if [ -z "$tag" -o "$tag" = tip ]; then
+			id=`printf '%s' "$hgid" | sed 's/[+ ].*//'`
+			printf '%s%s' -hg "$id"
+		fi
+
+		# Are there uncommitted changes?
+		# These are represented by + after the changeset id.
+		case "$hgid" in
+			*+|*+\ *) printf '%s' -dirty ;;
+		esac
+
+		# All done with mercurial
+		return
 	fi
 
-	# Update index only on r/w media
-	[ -w . ] && git update-index --refresh --unmerged > /dev/null
+	# Check for svn and a svn repo.
+	if rev=`svn info 2>/dev/null | grep '^Last Changed Rev'`; then
+		rev=`echo $rev | awk '{print $NF}'`
+		printf -- '-svn%s' "$rev"
 
-	# Check for uncommitted changes
-	if git diff-index --name-only HEAD | grep -v "^scripts/package" \
-	    | read dummy; then
-		printf '%s' -dirty
+		# All done with svn
+		return
 	fi
+}
 
-	# All done with git
+collect_files()
+{
+	local file res
+
+	for file; do
+		case "$file" in
+		*\~*)
+			continue
+			;;
+		esac
+		if test -e "$file"; then
+			res="$res$(cat "$file")"
+		fi
+	done
+	echo "$res"
+}
+
+if $scm_only; then
+	scm_version
 	exit
 fi
 
-# Check for mercurial and a mercurial repo.
-if hgid=`hg id 2>/dev/null`; then
-	tag=`printf '%s' "$hgid" | cut -d' ' -f2`
+if test -e include/config/auto.conf; then
+	source "$_"
+else
+	echo "Error: kernelrelease not valid - run 'make prepare' to update it"
+	exit 1
+fi
 
-	# Do we have an untagged version?
-	if [ -z "$tag" -o "$tag" = tip ]; then
-		id=`printf '%s' "$hgid" | sed 's/[+ ].*//'`
-		printf '%s%s' -hg "$id"
+# localversion* files in the build and source directory
+res="$(collect_files localversion*)"
+if test ! "$srctree" -ef .; then
+	res="$res$(collect_files "$srctree"/localversion*)"
+fi
+
+# CONFIG_LOCALVERSION and LOCALVERSION (if set)
+res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
+
+# scm version string if not at a tagged commit
+if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
+	# full scm version string
+	res="$res$(scm_version)"
+else
+	# apped a plus sign if the repository is not in a clean tagged
+	# state and  LOCALVERSION= is not specified
+	if test "${LOCALVERSION+set}" != "set"; then
+		scm=$(scm_version --short)
+		res="$res${scm:++}"
 	fi
-
-	# Are there uncommitted changes?
-	# These are represented by + after the changeset id.
-	case "$hgid" in
-		*+|*+\ *) printf '%s' -dirty ;;
-	esac
-
-	# All done with mercurial
-	exit
 fi
 
-# Check for svn and a svn repo.
-if rev=`svn info 2>/dev/null | grep '^Last Changed Rev'`; then
-	rev=`echo $rev | awk '{print $NF}'`
-	printf -- '-svn%s' "$rev"
-
-	# All done with svn
-	exit
-fi
+echo "$res"