c1a53c5
#!/bin/bash
c1a53c5
#
c1a53c5
# akmodbuild - Helper script for building kernel module SRPMs
c1a53c5
# Copyright (c) 2007 Thorsten Leemhuis <fedora@leemhuis.info>
c1a53c5
#
c1a53c5
# Permission is hereby granted, free of charge, to any person obtaining
c1a53c5
# a copy of this software and associated documentation files (the
c1a53c5
# "Software"), to deal in the Software without restriction, including
c1a53c5
# without limitation the rights to use, copy, modify, merge, publish,
c1a53c5
# distribute, sublicense, and/or sell copies of the Software, and to
c1a53c5
# permit persons to whom the Software is furnished to do so, subject to
c1a53c5
# the following conditions:
c1a53c5
#
c1a53c5
# The above copyright notice and this permission notice shall be
c1a53c5
# included in all copies or substantial portions of the Software.
c1a53c5
#
c1a53c5
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c1a53c5
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c1a53c5
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c1a53c5
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
c1a53c5
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
c1a53c5
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c1a53c5
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c1a53c5
#
c1a53c5
myprog="akmodsbuild"
c1a53c5
myver="0.5.6"
c1a53c5
c1a53c5
# defaults that might get overwritten by user:
c1a53c5
kernels="$(uname -r)"
c1a53c5
target="$(uname -m)"
c1a53c5
if [[ "${target}" == "armv7l" ]]; then
c1a53c5
  target="armv7hl"
c1a53c5
fi
c1a53c5
numberofjobs=$(grep -c processor /proc/cpuinfo 2> /dev/null)
c1a53c5
verboselevel=2
c1a53c5
outputdir="${PWD}"
c1a53c5
srpms=
c1a53c5
c1a53c5
init ()
c1a53c5
{
c1a53c5
	## startup checks
c1a53c5
	# prevent root-usage
c1a53c5
	if [[ -w / ]]; then
c1a53c5
		echo "ERROR: Not to be used as root; start as user or '${myprog}' instead." >&2
c1a53c5
		exit 1 
c1a53c5
	fi
c1a53c5
c1a53c5
	# do we have everything we need to build for the kernels in question?
c1a53c5
	for kernel in ${kernels}; do
c1a53c5
		if [[ ! -e /usr/src/kernels/${kernel}/Makefile ]] && [[ ! -e /usr/lib/modules/${kernel}/build/Makefile ]]; then
c1a53c5
			echo "ERROR: Files needed for building modules against kernel" >&2
c1a53c5
			echo " ${kernel} could not be found as the following" >&2
c1a53c5
			echo " directories are missing:"
c1a53c5
			echo " /usr/src/kernels/${kernel}/" >&2
c1a53c5
			echo " /usr/lib/modules/${kernel}/build/" >&2
c1a53c5
			exit 2
c1a53c5
		fi
c1a53c5
	done
c1a53c5
c1a53c5
	if [[ ! "${srpms}" ]]; then
c1a53c5
			echo "ERROR: Please provide a list of SRPM-files to build."
c1a53c5
			exit 2
c1a53c5
	fi
c1a53c5
c1a53c5
	# SRPMS available?
c1a53c5
	for srpm in ${srpms}; do
c1a53c5
		if [[ ! -r ${srpm} ]]; then
c1a53c5
			echo "ERROR: Can't find SRPM ${srpm}"
c1a53c5
			exit 1
c1a53c5
		fi
c1a53c5
	done
c1a53c5
c1a53c5
	# room to save things
c1a53c5
	if [[ ! -d "${outputdir}" ]]; then
c1a53c5
		echo "ERROR: ${outputdir} is not a directory" >&2
c1a53c5
		exit 1
c1a53c5
	elif [[ ! -w "${outputdir}" ]]; then
c1a53c5
		echo "ERROR: ${outputdir} is not a writable" >&2
c1a53c5
		exit 1
c1a53c5
	fi
c1a53c5
c1a53c5
c1a53c5
	# make sure this is a number
c1a53c5
	if ! (( ${numberofjobs} > 0 )); then
c1a53c5
		echo "Warning: using hardcoded defaut value for number of jobs"
c1a53c5
		numberofjobs=2
c1a53c5
	fi
c1a53c5
c1a53c5
	## preparations
c1a53c5
	# tmpdir
c1a53c5
	if ! tmpdir="$(mktemp -d -p /tmp ${myprog}.XXXXXXXX)" ; then 
c1a53c5
			echo "ERROR: Could create tempdir."
c1a53c5
			exit 1
c1a53c5
	fi
c1a53c5
c1a53c5
	# buildtreee
c1a53c5
	mkdir "${tmpdir}"/{BUILD,SOURCES,SPECS,SRPMS,RPMS,RPMS/"${target}"}
c1a53c5
c1a53c5
	# logfile
c1a53c5
	if [[ ! "${logfile}" ]] ; then
c1a53c5
		logfile="${tmpdir}/logfile"
c1a53c5
	fi
c1a53c5
c1a53c5
	if ( [[ -e "${logfile}" ]] && [[ ! -w "${logfile}" ]] ) || ! touch "${logfile}" ; then
c1a53c5
			echo "ERROR: Could not write logfile."
c1a53c5
			finally
c1a53c5
			exit 1
c1a53c5
	fi
c1a53c5
}
c1a53c5
c1a53c5
c1a53c5
finally()
c1a53c5
{
c1a53c5
	# kill background jobs if needed
c1a53c5
	if [[ "${watch_jobid}" ]]; then
c1a53c5
		kill "${watch_jobid}"
c1a53c5
	fi
c1a53c5
	if [[ "${rpmbuild_jobid}" ]]; then
c1a53c5
		kill "${rpmbuild_jobid}"
c1a53c5
	fi
c1a53c5
c1a53c5
	# remove tmpfiles
c1a53c5
	if [[ -d "${tmpdir}" ]]; then
c1a53c5
		rm -rf "${tmpdir}"
c1a53c5
	fi
c1a53c5
}
c1a53c5
trap "finally" 2
c1a53c5
c1a53c5
c1a53c5
akmods_echo()
c1a53c5
{
c1a53c5
	# where to output
c1a53c5
	local this_fd=${1}
c1a53c5
	shift
c1a53c5
c1a53c5
	# verboselevel
c1a53c5
	local this_verbose=${1}
c1a53c5
	shift
c1a53c5
c1a53c5
	if [[ "${1}" == "--not-logfile" ]]; then
c1a53c5
		local notlogfile=true
c1a53c5
		shift
c1a53c5
	fi
c1a53c5
c1a53c5
	# output to console
c1a53c5
	if (( ${verboselevel} >= ${this_verbose} )) ; then
c1a53c5
		echo "$@" >&${this_fd}
c1a53c5
	fi
c1a53c5
c1a53c5
	# global logfile
c1a53c5
	if [[ ! ${notlogfile} ]]; then
c1a53c5
		echo "$@" >> "${logfile}"
c1a53c5
	fi
c1a53c5
}
c1a53c5
c1a53c5
c1a53c5
watch_rpmbuild()
c1a53c5
{
c1a53c5
	# background function to show rpmbuild progress
c1a53c5
	# does't use akmods_echo here; this stage handles the output on its own
c1a53c5
	# (seperate process and there is no need to log this)
c1a53c5
	if (( ${verboselevel} == 2 )); then
c1a53c5
		tail --pid ${1} -n +1 -s 0.1 -f ${2} 2>/dev/null | grep --line-buffered -e '%prep' -e '%build' -e '%install' -e '%clean' |  while read line; do
c1a53c5
			if [[ "${line}" != "${line##*prep}" ]]; then
c1a53c5
				echo -n "prep "
c1a53c5
			elif [[ "${line}" != "${line##*build}" ]]; then
c1a53c5
				echo -n "build "
c1a53c5
			elif [[ "${line}" != "${line##*install}" ]]; then
c1a53c5
				echo -n "install "
c1a53c5
			elif [[ "${line}" != "${line##*clean}" ]]; then
c1a53c5
				echo -n "clean; "
c1a53c5
				# last linefeed is done by the caller
c1a53c5
			fi
c1a53c5
		done
c1a53c5
	elif (( ${verboselevel} > 2 )); then
c1a53c5
		tail --pid ${1} -n +1 -s 0.1 -f ${2}
c1a53c5
	fi
c1a53c5
}
c1a53c5
c1a53c5
process_srpm()
c1a53c5
{
c1a53c5
	local source_rpm="${1}"
c1a53c5
c1a53c5
	# status info
c1a53c5
	akmods_echo 1 2 -n "* Rebuilding ${source_rpm} for kernel(s) ${kernels}: "
c1a53c5
c1a53c5
	# kick off rebuild into background
c1a53c5
	/usr/bin/time --format='%x' --output="${tmpdir}/.jobexit" rpmbuild \
c1a53c5
		--define "_topdir     ${tmpdir}/" \
c1a53c5
		--define "_buildtree  ${tmpdir}/BUILD" \
c1a53c5
		--define "_specdir    ${tmpdir}/SPECS" \
c1a53c5
		--define "_sourcedir  ${tmpdir}/SOURCES" \
c1a53c5
		--define "_srcrpmdir  ${tmpdir}/SRPMS" \
c1a53c5
		--define "_rpmdir     ${tmpdir}/RPMS" \
c1a53c5
		--define "_smp_mflags -j${numberofjobs}" \
c1a53c5
		--define "kernels     ${kernels}" \
c1a53c5
		--target ${target} \
c1a53c5
		--rebuild "${source_rpm}" 2>&1 | tee -a "${logfile}" > "${tmpdir}/.joblog"  &
c1a53c5
c1a53c5
	local rpmbuild_jobid=$!
c1a53c5
c1a53c5
	# show progress
c1a53c5
	if (( ${verboselevel} >= 2 )); then
c1a53c5
		watch_rpmbuild ${rpmbuild_jobid} "${tmpdir}/.joblog" 2> /dev/null &
c1a53c5
		local watch_jobid=$!
c1a53c5
	fi
c1a53c5
c1a53c5
	# wait for rpmbuild
c1a53c5
	wait ${rpmbuild_jobid}
c1a53c5
	local rpmbuild_returncode=$(tail -n 1 "${tmpdir}/.jobexit")
c1a53c5
	unset rpmbuild_jobid
c1a53c5
c1a53c5
	# give watch_rpmbuild a moment to catch up; kill it if it does not
c1a53c5
	if (( ${verboselevel} >= 2 )); then
c1a53c5
		sleep 0.5
c1a53c5
		kill ${watch_jobid} &> /dev/null
c1a53c5
		unset watch_jobid
c1a53c5
	fi
c1a53c5
c1a53c5
	# did rpmbuild succeed?
c1a53c5
	if (( ${rpmbuild_returncode} != 0 )); then
c1a53c5
		# linefeed:
c1a53c5
		akmods_echo 1 2 ""
c1a53c5
c1a53c5
		akmods_echo 2 2 --not-logfile "rpmbuild failed with errorcode ${rpmbuild_returncode}; last 35 Lines of log:"
c1a53c5
		akmods_echo 2 2 --not-logfile "--- "
c1a53c5
		tail -n 35 "${tmpdir}/.joblog" >&2
c1a53c5
		akmods_echo 2 2  --not-logfile "---"
c1a53c5
		return ${rpmbuild_returncode}
c1a53c5
	fi
c1a53c5
c1a53c5
	# finish status for watch_rpmbuild
c1a53c5
	if (( ${verboselevel} >= 2 )); then
c1a53c5
		akmods_echo 1 2 -n "Successfull; "
c1a53c5
	fi
c1a53c5
c1a53c5
	local rpms_built="$(cd "${tmpdir}"/RPMS/"${target}" ; echo *)"
c1a53c5
c1a53c5
	if ! mv "${tmpdir}/RPMS/${target}/"* "${outputdir}" ; then
c1a53c5
		# linefeed:
c1a53c5
		akmods_echo 1 2 ""
c1a53c5
c1a53c5
		akmods_echo 2 2 "Failed to move ${tmpdir}/RPMS/${target}/"* "to ${outputdir}"
c1a53c5
		return 128
c1a53c5
	fi
c1a53c5
c1a53c5
	if (( ${verboselevel} == 1 )); then
c1a53c5
		for rpm in ${rpms_built}; do
c1a53c5
			echo "${outputdir%%/}/${rpm}"
c1a53c5
		done
c1a53c5
	elif (( ${verboselevel} >= 2 )); then
c1a53c5
		akmods_echo 1 2  "Saved ${rpms_built} in ${outputdir%%/}/"
c1a53c5
	fi
c1a53c5
c1a53c5
c1a53c5
	# finished
c1a53c5
	return 0
c1a53c5
}
c1a53c5
c1a53c5
myprog_help ()
c1a53c5
{
c1a53c5
	echo "Rebuilds kmod SRPM(s)"
c1a53c5
	echo $'\n'"Usage: ${myprog} [OPTIONS] <SRPMS>"
c1a53c5
	echo $'\n'"Options:"
c1a53c5
	echo " -k, --kernels         -- build for kernel-versions (output from 'uname -r')"
c1a53c5
	echo " -l, --logfile <file>  -- save rpmduild output to <file>"
c1a53c5
	echo " -o, --outputdir <dir> -- save rpms and logs here (current directory)"
c1a53c5
	echo " -t, --target          -- target-arch (output from 'uname -m')"
c1a53c5
	echo " -v, --verbose         -- increase verboseness"
c1a53c5
	echo " -q, --quiet           -- be more quiet"
c1a53c5
	echo " -h, --help            -- show usage"
c1a53c5
	echo " -V, --version         -- show version"
c1a53c5
}
c1a53c5
c1a53c5
while [ "${1}" ] ; do
c1a53c5
	case "${1}" in
c1a53c5
		-k|--kernels)
c1a53c5
			shift
c1a53c5
			if [[ ! "${1}" ]] ; then
c1a53c5
				echo "ERROR: Please provide kernel-version(s) to build for together with --kernel" >&2
c1a53c5
				exit 1
c1a53c5
			fi
c1a53c5
			kernels="${1}"
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-l|--logfile)
c1a53c5
			shift
c1a53c5
			if [[ ! "${1}" ]]; then
c1a53c5
				echo "ERROR: Please provide a filename together with --logfile" >&2
c1a53c5
				exit 1
c1a53c5
			fi
c1a53c5
			logfile="${1}"
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-o|--outputdir)
c1a53c5
			shift
c1a53c5
			if [[ ! "${1}" ]]; then
c1a53c5
				echo "ERROR: Please provide the output directory together with --outputdir" >&2
c1a53c5
				exit 1
c1a53c5
			fi
c1a53c5
			outputdir="${1}"
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-t|--target)
c1a53c5
			shift
c1a53c5
			if [[ ! "${1}" ]] ; then
c1a53c5
				echo "ERROR: Please provide the target-arch together with --target" >&2
c1a53c5
				exit 1
c1a53c5
			fi
c1a53c5
			target="${1}"
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-v|--verbose)
c1a53c5
			let verboselevel++
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-q|--quiet)
c1a53c5
			let verboselevel--
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
		-h|--help)
c1a53c5
			myprog_help
c1a53c5
			exit 0
c1a53c5
			;;
c1a53c5
		-V|--version)
c1a53c5
			echo "${myprog} ${myver}"
c1a53c5
			exit 0
c1a53c5
			;;
c1a53c5
		--*)
c1a53c5
			echo "Error: Unknown option '${1}'." >&2
c1a53c5
			myprog_help >&2
c1a53c5
			exit 2
c1a53c5
			;;
c1a53c5
		*)
c1a53c5
			srpms="${srpms} ${1}"
c1a53c5
			shift
c1a53c5
			;;
c1a53c5
	esac
c1a53c5
done
c1a53c5
c1a53c5
# sanity checks
c1a53c5
init
c1a53c5
c1a53c5
# go
c1a53c5
for srpm in ${srpms}; do
c1a53c5
	process_srpm ${srpm}
c1a53c5
	returncode=$?
c1a53c5
c1a53c5
	if (( ${returncode} != 0 )); then
c1a53c5
		finally
c1a53c5
		exit ${returncode}
c1a53c5
	fi
c1a53c5
done
c1a53c5
c1a53c5
# finished
c1a53c5
finally
c1a53c5
c1a53c5
exit 0