Skip to content

Instantly share code, notes, and snippets.

@protonesso

protonesso/makerpm

Created Jul 22, 2019
Embed
What would you like to do?
Build RPM packages with more comfort
#!/bin/bash
umask 022
unalias -a
pushd () { command pushd "$@" > /dev/null; }
popd () { command popd "$@" > /dev/null; }
msg() { echo -e "\033[1;32m >>> \033[0m$@"; }
msgtwo() { echo -e "\033[1;35m * \033[0m$@"; }
msgthree() { echo -e "\033[1;32m * \033[0m$@"; }
warn() { echo -e "\033[1;33m ??? \033[0m$@"; }
warntwo() { echo -e "\033[1;33m * \033[0m$@"; }
die() {
case $1 in
-2)
MINIMSG=1
;;
-3)
MINIMSG=1
RMCONFLICTS=1
;;
esac
if [ "$MINIMSG" = "1" ]; then
echo -e "\033[1;31m * \033[0m${@:2}"
else
echo -e "\033[1;31m >>> \033[0m${@}"
fi
if [ "$CLEAN" = "yes" ]; then
rm -rf "$WORK"
fi
exit 1
}
get_filename() {
local ABSOLUTE=""
if [ "$1" = "-a" ]; then
ABSOLUTE=1
shift
fi
if [[ $1 =~ ^(http|https|ftp|file)://.*/(.+) ]]; then
echo "$SRCDEST/${BASH_REMATCH[2]}"
else
if [ "$ABSOLUTE" ]; then
echo $TOPDIR/$1
else
echo $1
fi
fi
}
run_strip() {
local fs="$1"
find "$fs/" -type f 2>/dev/null | while read -r binary ; do
case "$(file -bi $binary)" in
*application/x-sharedlib*) # Libraries (.so)
${CROSS_COMPILE}strip --strip-unneeded "$binary" ;;
*application/x-pie-executable*) # Libraries (.so)
${CROSS_COMPILE}strip --strip-unneeded "$binary" ;;
*application/x-archive*) # Libraries (.a)
${CROSS_COMPILE}strip --strip-debug "$binary" ;;
*application/x-executable*) # Binaries
${CROSS_COMPILE}strip --strip-all "$binary" ;;
*)
continue ;;
esac
done
}
parse_options() {
for myopts in ${OPTIONS[@]} ${options[@]}; do
case $myopts in
emptydirs)
OPT_EMPTYDIRS=1
;;
~emptydirs)
OPT_EMPTYDIRS=0
;;
strip)
OPT_STRIP=1
;;
~strip)
OPT_STRIP=0
;;
makeflags)
OPT_MAKEFLAGS=1
;;
~makeflags)
OPT_MAKEFLAGS=0
;;
locales)
OPT_LOCALES=1
;;
~locales)
OPT_LOCALES=0
;;
docs)
OPT_DOCS=1
;;
~docs)
OPT_DOCS=0
;;
~bootstrap)
OPT_BOOTSTRAP=0
;;
bootstrap)
OPT_BOOTSTRAP=1
;;
~ccache)
OPT_CCACHE=0
;;
ccache)
OPT_CCACHE=1
;;
esac
done
}
find_portdir() {
local pkg
pkg="$1"
for myrepos in ${REPOS[@]}; do
if [ -d "$myrepos/$pkg" ]; then
echo "$myrepos/$pkg"
fi
done
}
check_root() {
if [[ $EUID -ne 0 ]]; then
die "You must be root to run 'kagami'"
fi
}
source_config() {
if [ -f "$CONFIG" ]; then
source "$CONFIG"
else
die "Failed to source the configuration file"
fi
if [ -z "$BARCH" ]; then
BARCH="$(uname -m)"
fi
}
source_recipe() {
unset name version release options backup source noextract triggers
if [ -f "$TOPDIR/KagamiBuild" ]; then
MYPORT="$TOPDIR/KagamiBuild"
elif [ -f "$TOPDIR/KBUILD" ]; then
MYPORT="$TOPDIR/KBUILD"
elif [ -f "$TOPDIR/Pkgfile" ]; then
MYPORT="$TOPDIR/Pkgfile"
else
die "Failed to source the package recipe"
fi
source "$MYPORT"
depends="$(grep "^# Depends on[[:blank:]]*:" "$MYPORT" | sed 's/^# Depends on[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++')"
conflicts="$(grep "^# Conflicts with[[:blank:]]*:" "$MYPORT" | sed 's/^# Conflicts with[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++' | sed -e 's/ /, /g')"
provides="$(grep "^# Provides[[:blank:]]*:" "$MYPORT" | sed 's/^# Provides[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++' | sed -e 's/ /, /g')"
obsoletes="$(grep "^# Obsoletes[[:blank:]]*:" "$MYPORT" | sed 's/^# Obsoletes[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++' | sed -e 's/ /, /g')"
summary="$(grep "^# Description[[:blank:]]*:" "$MYPORT" | sed 's/^# Description[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++')"
url="$(grep "^# URL[[:blank:]]*:" "$MYPORT" | sed 's/^# URL[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++')"
group="$(grep "^# Group[[:blank:]]*:" "$MYPORT" | sed 's/^# Group[[:blank:]]*:[[:blank:]]*//' | tr ' ' ' ' | awk '!a[$0]++')"
if [ -z "$name" ]; then
die -2 "The name of package is not set"
elif [ -z "$version" ]; then
die -2 "The version of package is not set"
elif [ -z "$release" ]; then
die -2 "The release of package is not set"
elif [ -z "$summary" ]; then
die -2 "The description of package is not set"
fi
}
download_file() {
local url="$1"
if [ ! "`type -p curl`" ]; then
die -2 "curl wasn't found"
fi
msgtwo "Downloading $url"
LOCAL_FILENAME=`get_filename $url`
LOCAL_FILENAME_PARTIAL="$LOCAL_FILENAME.partial"
cmd="-L -o $LOCAL_FILENAME_PARTIAL $CURLOPTS"
resumecmd="-C -"
fullcmd="curl $cmd"
RESUME="no"
if [ -f "$LOCAL_FILENAME_PARTIAL" ]; then
warntwo "Partial download found, trying to resume"
RESUME="yes"
fullcmd="$fullcmd $resumecmd"
fi
error=1
if [ $error != 0 ]; then
while true; do
$fullcmd $1
error=$?
if [ $error != 0 ] && [ "$RESUME" = "yes" ]; then
warn "Partial download failed, restarting"
rm -f "$LOCAL_FILENAME_PARTIAL"
RESUME="yes"
else
break
fi
done
fi
if [ $error != 0 -o ! -f "$LOCAL_FILENAME_PARTIAL" ]; then
die -2 "Downloading '${1}' failed."
fi
mv -f "$LOCAL_FILENAME_PARTIAL" "$LOCAL_FILENAME"
}
download_source() {
local FILE LOCAL_FILENAME
if [ -n "$source" ]; then
for FILE in ${source[@]}; do
LOCAL_FILENAME=`get_filename $FILE`
if [ ! -e $LOCAL_FILENAME ]; then
if [ "$LOCAL_FILENAME" = "$FILE" ]; then
die -2 "Source file '$LOCAL_FILENAME' not found (can not be downloaded, URL not specified)."
else
download_file $FILE
fi
fi
done
fi
}
unpack_source() {
if [ "${#source[@]}" -gt 0 ]; then
for FILE in ${source[@]}; do
FILENAME="$(basename $FILE)"
LOCAL_FILENAME=`get_filename $FILE`
for NOEXT in ${noextract[@]}; do
if [ "$NOEXT" = "$FILENAME" ]; then
nxt=1
break
fi
done
if [ "$FILENAME" != "$FILE" ] && [ "$nxt" != 1 ]; then
case $LOCAL_FILENAME in
*.tar|*.tar.gz|*.tar.Z|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.lzma|*.tar.lz|*.tlz|*.tar.lz4|*.tar.zst|*.tar.zstd|*.zip|*.rpm|*.7z)
msgtwo "Extacting $FILENAME to the sources directory"
bsdtar -p -o -C "$SRC" -xf $LOCAL_FILENAME || die -2 "Failed to extract $FILENAME to the sources directory"
;;
*)
msgtwo "Copying $FILENAME to the sources directory"
cp $LOCAL_FILENAME "$SRC" || die -2 "Failed to copy $FILENAME to the sources directory"
;;
esac
fi
nxt=
done
fi
}
compile_package() {
local BUILD_SUCCESS="no"
if [ "$OPT_MAKEFLAGS" != "0" ]; then
export MAKEFLAGS="$MKOPTS"
export NINJA_OPTIONS="$MKOPTS"
else
unset MAKEFLAGS NINJA_OPTIONS MKOPTS
fi
if [ "$USE_BOOTSTRAP" != "yes" ]; then
if [ "$OPT_CCACHE" != "0" ]; then
if type ccache > /dev/null 2>&1; then
if [ -z "$CC" ]; then
export CC="ccache gcc"
else
export CC="ccache $CC"
fi
if [ -z "$CXX" ]; then
export CXX="ccache g++"
else
export CXX="ccache $CXX"
fi
fi
fi
fi
if type build > /dev/null 2>&1; then
msgtwo "Compiling package"
pushd "$SRC"
(set -e; build)
if [ $? = 0 ]; then
BUILD_SUCCESS="yes"
fi
if [ "$BUILD_SUCCESS" != "yes" ]; then
die -2 "Failed to compile package"
fi
popd
fi
pushd "$PKG"
msgtwo "Removing junk from package"
rm -rf {,usr/}{,local/}{,share/}info usr{,/local}{,/share},opt/*}/info
rm -rf {,usr/}{,local/}lib/charset.alias
find . -name "*.la" -print0 | xargs -0 rm -rf
find . -name "*.pod" -print0 | xargs -0 rm -rf
find . -name ".packlist" -print0 | xargs -0 rm -rf
if [ "$OPT_EMPTYDIRS" = "0" ]; then
msgtwo "Removing empty directories"
find . -type d -empty -print0 | xargs -0 rm -rf
fi
if [ "$OPT_LOCALES" = "0" ]; then
msgtwo "Removing locales"
rm -rf {,usr/}{,local/}{,share/}locale
fi
if [ "$OPT_DOCS" = "0" ]; then
msgtwo "Removing documentation"
rm -rf {,usr/}{,local/}{,share/}doc usr/{,local/}{,share/}{doc,gtk-doc} opt/*/{doc,gtk-doc}
fi
msgtwo "Compressing manual pages"
find . -type f -path "*/man/man*/*" | while read FILE; do
if [ "$FILE" = "${FILE%%.gz}" ]; then
pigz -9 "$FILE"
fi
done
find . -type l -path "*/man/man*/*" | while read FILE; do
TARGET=`readlink -n "$FILE"`
TARGET="${TARGET##*/}"
TARGET="${TARGET%%.gz}.gz"
rm -f "$FILE"
FILE="${FILE%%.gz}.gz"
DIR=`dirname "$FILE"`
if [ -e "$DIR/$TARGET" ]; then
ln -sf "$TARGET" "$FILE"
fi
done
if [ "$OPT_STRIP" != "0" ]; then
msgtwo "Removing debug information from binary files"
run_strip "$PKG"
fi
popd
}
create_package() {
RPMSOURCEDIR="`rpm --eval=%_topdir`"
RPMPKG="${RPMSOURCEDIR}/RPMS/$BARCH/$name-$version-${release}mdk.$BARCH.rpm"
SPECFILE="$WORK/$name.spec"
mkdir -p "$RPMSOURCEDIR"/{BUILD,RPMS/$BARCH,SOURCES,SPECS,SRPMS}
pushd "$WORK"
msgtwo "Creating package summary"
echo "Name: $name" >> "$SPECFILE"
echo "Version: $version" >> "$SPECFILE"
echo "Release: $release%{distro}" >> "$SPECFILE"
echo "Summary: $summary" >> "$SPECFILE"
echo "License: FIXME" >> "$SPECFILE"
if [ -n "$url" ]; then
echo "URL: $url" >> "$SPECFILE"
fi
if [ -n "$group" ]; then
echo "Group: $group" >> "$SPECFILE"
fi
if [ -n "$conflicts" ]; then
echo "Conflicts: $conflicts" >> "$SPECFILE"
fi
if [ -n "$provides" ]; then
echo "Provides: $provides" >> "$SPECFILE"
fi
if [ -n "$obsoletes" ]; then
echo "Obsoletes: $obsoletes" >> "$SPECFILE"
fi
if [ -n "$depends" ]; then
for mydeps in ${depends[@]}; do
echo "Requires: $mydeps" >> "$SPECFILE"
done
fi
echo >> "$SPECFILE"
echo '%description' >> "$SPECFILE"
echo >> "$SPECFILE"
if [ -f "$TOPDIR/$name.pre-install" ]; then
echo "%pre" >> "$SPECFILE"
cat "$TOPDIR/$name.pre-install" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
if [ -f "$TOPDIR/$name.post-install" ]; then
echo "%post" >> "$SPECFILE"
cat "$TOPDIR/$name.post-install" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
if [ -f "$TOPDIR/$name.pre-remove" ]; then
echo "%preun" >> "$SPECFILE"
cat "$TOPDIR/$name.pre-remove" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
if [ -f "$TOPDIR/$name.post-remove" ]; then
echo "%postun" >> "$SPECFILE"
cat "$TOPDIR/$name.post-remove" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
if [ -f "$TOPDIR/$name.install-trigger" ]; then
echo "%filetriggerin -- $triggers" >> "$SPECFILE"
cat "$TOPDIR/$name.install-trigger" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
if [ -f "$TOPDIR/$name.remove-trigger" ]; then
echo "%filetriggerun -- $triggers" >> "$SPECFILE"
cat "$TOPDIR/$name.remove-trigger" >> "$SPECFILE"
echo >> "$SPECFILE"
fi
echo '%files' >> "$SPECFILE"
echo '%defattr(-,root,root)' >> "$SPECFILE"
if [ -n "$backup" ]; then
for mybackup in ${backup[@]}; do
echo "%config(noreplace) /${mybackup}" >> "$SPECFILE"
done
fi
echo '/*' >> "$SPECFILE"
echo >> "$SPECFILE"
echo '%changelog' >> "$SPECFILE"
echo '* Sun Sep 1 2002 Suzuki Tokugawa <root@localhost>' >> "$SPECFILE"
echo '- Initial package' >> "$SPECFILE"
msgtwo "Building RPM package"
rpmbuild -bb --define "distro mdk" --target $BARCH --buildroot="$PKG" --clean "$SPECFILE" &>/dev/null
mv "$RPMPKG" "$TARGET"
popd
msg "Package was successfuly built!"
}
rpm_build() {
source_recipe
if [ -z "$PKGDEST" ]; then
PKGDEST="$TOPDIR"
fi
if [ -z "$SRCDEST" ]; then
SRCDEST="$TOPDIR"
fi
TARGET="$PKGDEST/$name-$version-${release}mdk.$BARCH.rpm"
if [ "$FORCE" = "yes" ]; then
rm -rf "$TARGET"
fi
if [ ! -f "$TARGET" ]; then
msg "Building package: '${name}'"
WORK="$PWD/work"
PKG="$WORK/pkg"
SRC="$WORK/src"
rm -rf "$WORK"
mkdir -p "$PKG" "$SRC"
parse_options
if [ "$USEBOOTSTRAP" = "yes" ]; then
if [ -z "$OPT_BOOTSTRAP" ]; then
die -2 "This package couldn't be built in the cross environment"
elif [ "$OPT_BOOTSTRAP" = "0" ]; then
die -2 "This package couldn't be built in the cross environment"
fi
fi
if [ "$DOWNLOAD" != "no" ]; then
download_source
fi
if [ "$UNPACK" != "no" ]; then
unpack_source
fi
if [ "$BUILD" != "no" ]; then
compile_package
fi
if [ "$PACKAGE" != "no" ]; then
create_package
if [ "$CLEAN" = "yes" ]; then
rm -rf "$WORK"
fi
else
if [ "$CLEAN" = "yes" ]; then
rm -rf "$WORK"
fi
fi
fi
}
main() {
export LC_ALL=POSIX
export VERSION="0.1"
export TOPDIR="$PWD"
CONFIG="/etc/kagami.conf"
OPTIONS=('emptydirs' 'strip' 'makeflags' 'locales' 'docs' 'ccache')
REPOS=('/usr/ports')
FORCE="no"
VERBOSE="no"
NEEDED="no"
CLEAN="yes"
DOWNLOAD="yes"
UNPACK="yes"
BUILD="yes"
PACKAGE="yes"
DEPENDS="yes"
SCRIPTS="yes"
TRIGGERS="yes"
while [ "$1" ]; do
case $1 in
--help|-h)
usage
;;
--version|-v)
version
;;
--config=*)
CONFIG="${1#*=}"
;;
--root=*)
ROOT="${1#*=}"
;;
--force|-f)
FORCE="yes"
;;
--clean|-c)
CLEAN="yes"
;;
--verbose|-V)
VERBOSE="yes"
;;
--no-download|-nd)
DOWNLOAD="no"
;;
--no-unpack|-nu)
UNPACK="no"
;;
--no-build|-nb)
BUILD="no"
;;
--no-package|-np)
PACKAGE="no"
;;
--no-deps|-dd)
DEPENDS="no"
;;
--no-scripts|-ss)
SCRIPTS="no"
;;
--no-triggers|-tt)
TRIGGERS="no"
;;
--needed|-nn)
NEEDED="yes"
;;
*)
PKG="$1"
;;
esac
shift
done
check_root
source_config
if [ -n "$PKG" ]; then
rpm_emerge "$PKG"
else
rpm_build
fi
}
for opts in "$@"; do
main $opts
done
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.