1338 lines
47 KiB
Bash
Executable file
1338 lines
47 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# Part of the SMLinux distribution
|
|
# http://git.pktsurf.in/smlinux
|
|
#
|
|
# /bin/bldpkg version 0.105
|
|
# Bash script to build SMLinux-specific packages
|
|
#
|
|
# Copyright (c) 2022-2024 PktSurf <smlinux@pktsurf.in>
|
|
#
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
############
|
|
|
|
set -e
|
|
|
|
err() {
|
|
printf "**ERROR**\n$@\n"
|
|
exit 1
|
|
}
|
|
|
|
info() {
|
|
printf "[INFO] $@\n"
|
|
}
|
|
|
|
warn() {
|
|
printf "[WARNING]\n$@\n"
|
|
}
|
|
|
|
# Determine whether we are using bash version 4 and later. If not, exit.
|
|
if [[ -z $BASH_VERSINFO ]] ; then
|
|
err "Is this really a GNU bash shell?"
|
|
elif ((BASH_VERSINFO[0] < 4)) ; then
|
|
err "Bldpkg requires a minimum of GNU bash shell version 4 to run"
|
|
fi
|
|
|
|
getbuildfileanddir() {
|
|
srcdir="$PWD"
|
|
buildfile="smbuild"
|
|
}
|
|
|
|
# Function to error out the build in absence of a build file
|
|
nopackagebuildfileerror() {
|
|
err "No package build file to source from!
|
|
Was expecting '$buildfile' to be present inside this directory '$PWD'.
|
|
Try -f <build_file> if your build file has a different name (Not recommended)
|
|
If you want to autobuild packages, use -a"
|
|
}
|
|
|
|
startdate() {
|
|
# Time when the build commenced. Note: elapsed time is logged by the runtime function way below.
|
|
# This output goes into package build summary.
|
|
commencedate=$(date '+%a, %d %b %Y, %T')
|
|
}
|
|
|
|
htmloutput() {
|
|
# If htmloutput is set to 1, echo $app, $version and $build as file names inside the parent build directory.
|
|
# This will output into an HTML file so that the basic status of the build process (whether started, stopped,
|
|
# interrupted or failed) can be viewed in the web browser.
|
|
if [[ $htmloutput = 1 ]] ; then
|
|
|
|
if [[ -n $autobuildmode ]] ; then
|
|
cat << EOF >> "$parenttmp/BUILDMONITOR"
|
|
<b>$commencedate | Building package $currentpkgnumber / $totalpkgnumber: <i><a href="/smlinux/pkgresults?pkg=$app&smver=1.0&arch=all&resultnum=25">$app $version</a></i></b>
|
|
EOF
|
|
else
|
|
cat << EOF >> "$parenttmp/BUILDMONITOR"
|
|
<b>$commencedate | Building package <i><a href="/smlinux/pkgresults?pkg=$app&smver=1.0&arch=all&resultnum=25">$app $version</a></i></b>
|
|
EOF
|
|
fi
|
|
|
|
touch "$parenttmp/BUILDING"
|
|
fi
|
|
}
|
|
|
|
# Function to generate help message
|
|
help() {
|
|
cat << EOF
|
|
Bash script for building SMLinux-compatible packages from source.
|
|
Any option used as an argument with this script overrides the
|
|
corresponding option, if present, in /etc/bldpkg.conf or '$HOME/.bldpkg.conf'
|
|
|
|
If no arguments are provided, this script attempts to first look for
|
|
a build file called "smbuild" and starts the build.
|
|
|
|
For example, if the package source directory is $HOME/smlinux/alsa-lib, this script will
|
|
look and source from 'smbuild' and build alsa-lib package
|
|
|
|
# pwd
|
|
/home/user/smlinux/alsa-lib
|
|
|
|
# ls
|
|
smbuild alsa-lib-1.0.tar.gz
|
|
|
|
# bldpkg
|
|
Building package 'alsa-lib' version '1.x'...
|
|
...build output...
|
|
|
|
Usage:
|
|
|
|
-a Switch mode to autobuild. Default is to build individual packages.
|
|
Requires sections variable in bldpkg.conf to be defined
|
|
|
|
-d Produce a package with debug symbols preserved, i.e., don't strip
|
|
resulting ELF objects. Uses -g3 by default
|
|
|
|
-e Extract the package installer file in the user's PWD if the build
|
|
completes successfully
|
|
|
|
-f <file> Name of an alternate build file to source build variables. Should
|
|
be compatible with standard SMLinux package build file format
|
|
|
|
-g Generate SHA512 checksums of all tarballs and patches in the current
|
|
directory, insert them into the package build file and exit
|
|
|
|
-h Show this help message
|
|
|
|
-j <N> Provide a number of jobs to be run simultaneously
|
|
|
|
-r Resume the build by skipping all commands in prepbuilddir and execute
|
|
build() function. Presumes that the package was completely extracted
|
|
in the build directory, and patches, if any, were applied and executes
|
|
stuff inside build().
|
|
|
|
-s Display build summary. A summary is produced whenever a build is either
|
|
interrupted, exits cleanly or aborts due to a build error
|
|
|
|
-t Build inside tmpfs directory
|
|
|
|
-v Enable verbose compile
|
|
|
|
-x Invoke bash shell command trace mode. This one isn't akin to running
|
|
bash -x <script> but close enough, because the mode is inserted half-way
|
|
while bldpkg is running
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# Function to validate the build file.
|
|
validatebldfile() {
|
|
buildfile="${setbuildfile:-smbuild}"
|
|
|
|
if [[ ! -f $buildfile ]] ; then
|
|
err "No build file to validate from!"
|
|
else
|
|
source $buildfile
|
|
fi
|
|
|
|
for reqvars in app version build homepage desc requires ; do
|
|
[[ ! ${!reqvars} ]] && err "Required variable '$reqvars' is not set in $buildfile!"
|
|
done
|
|
|
|
# Check if 'Maintainer' comment exists
|
|
if ! grep -q "^# Maintainer" "$buildfile" ; then
|
|
err "Please provide a Maintainer name and email as a comment at the top of the build file"
|
|
|
|
# Validate $app
|
|
elif ! echo "$app" | grep -E -q '[a-z0-9-]+'; then
|
|
err "Only lower case, numeric characters and dash allowed in the 'app' variable in the build file."
|
|
|
|
# Validate $version
|
|
elif ! echo "$version" | grep -E -q '[a-z0-9.]+'; then
|
|
err "Only lower case, numeric characters and a period allowed in the 'version' variable in the build file."
|
|
|
|
# Validate $homepage
|
|
elif ! echo "$homepage" | grep -E -q '(https|http|ftp)://[^"]+' ; then
|
|
err "Invalid URL in the 'homepage' variable in the build file."
|
|
|
|
# Validate $desc using bash shell's ability to count variable length
|
|
elif [[ ${#desc} -gt 100 ]] ; then
|
|
err "Package description should not exceed 100 characters in the build file."
|
|
|
|
# Check if prepbuilddir() function exists in the build file
|
|
elif ! grep -q '^prepbuilddir() {' "$buildfile" ; then
|
|
err "'prepbuilddir()' function does not exist in your build file."
|
|
|
|
# Check if build() function exists in the build file
|
|
elif ! grep -q '^build() {' "$buildfile" ; then
|
|
err "'build()' function does not exist in your build file."
|
|
fi
|
|
|
|
# Validate the download variable separately because it's optional
|
|
if [[ -n $download ]] ; then
|
|
if ! echo "$download" | grep -E -q '(https|http|ftp)://[^"]+' ; then
|
|
err "Invalid URL in the 'download' variable in the build file."
|
|
fi
|
|
fi
|
|
|
|
# Check integrity of files defined in sha512sums variable which is expected
|
|
# in nearly every single package build file
|
|
if [[ -z $sha512sums ]] ; then
|
|
err "Please run 'bldpkg -g' to add sha512sums into '$buildfile'!"
|
|
fi
|
|
}
|
|
|
|
verifychecksums() {
|
|
eval sums=\"\$sha512sums\"
|
|
info "Verifying SHA512 checksums against source files..."
|
|
IFS=$'\n'
|
|
for src in $sums; do
|
|
if ! echo $src | sha512sum -c ; then
|
|
err "Checksums failed to match!"
|
|
fi
|
|
done
|
|
unset IFS
|
|
}
|
|
|
|
# Function to validate MAKEFLAGS passed on by getopts
|
|
validatemakeflags() {
|
|
local makeflags="$1"
|
|
local makeflags0="${makeflags/-j/}"
|
|
if ! echo "$makeflags0" | grep -E -q '^[0-9]+$' ; then
|
|
err "Invalid MAKEFLAGS '$makeflags0'!"
|
|
fi
|
|
}
|
|
|
|
# Function for applying patches to the build in a more efficient manner
|
|
# For use inside a package build file
|
|
applypatch() {
|
|
# Take patch file name as the first argument
|
|
local patchfile="$1"
|
|
|
|
# Perform a simple check
|
|
if [[ -z $patchfile ]] ; then
|
|
err "Please provide valid patch file name"
|
|
elif [[ ! -f $patchfile ]] ; then
|
|
err "Patch file '$patchfile' not found inside $srcdir!"
|
|
fi
|
|
|
|
# Get relative path of the patch file
|
|
local relativepath=${patchfile##*/}
|
|
info "Applying patch '$relativepath'.."
|
|
|
|
# We use if/else to determine if the patch applied successfully
|
|
if ! patch -p1 < "$patchfile" ; then
|
|
err "Failed to apply patch file '$patchfile'"
|
|
fi
|
|
}
|
|
|
|
# Function to specifically match arrays inside a value. This function will be used later on to perform
|
|
# package and directory matches using certain conditions. Note: "${ARRAY[@]}" =~ "${VARIABLE}" isn't fool-proof.
|
|
inarray() {
|
|
local n=$1 h
|
|
shift
|
|
for h ; do
|
|
[[ $n = "$h" ]] && return
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Function to validate compilers.
|
|
# Usage: compilertestfile "$CC" "$CFLAGS" cc-test.c cc-test
|
|
compilertestfile() {
|
|
local compileraddonflags="-O0 -Werror -static"
|
|
local compiler="$1"
|
|
local compilerflags="$2"
|
|
local compilerfile="$3"
|
|
local compileroutput="$4"
|
|
|
|
printf "[INFO] Validating compiler '$compiler'... "
|
|
|
|
# Inside a function, validate the compilers that have been set as CC and CXX in bldpkg.conf
|
|
# A small C/CXX program is being used to print a random number
|
|
|
|
# Generate a random number using shuf. We'll output this same number inside the C/CXX program.
|
|
# If the numbers match, the compiler looks sane, else error out.
|
|
local randomnum="$(shuf -i 100-9999 -n1)"
|
|
|
|
cat << EOF > "$parenttmp/$compilerfile"
|
|
#include <stdio.h>
|
|
int main() { printf("$randomnum\n"); }
|
|
EOF
|
|
|
|
$compiler $compilerflags -o "$parenttmp/$compileroutput" $compileraddonflags "$parenttmp/$compilerfile"
|
|
local finaloutput="$($parenttmp/$compileroutput)"
|
|
|
|
# Error out if the outputs don't match
|
|
if [[ $finaloutput != $randomnum ]] ;then
|
|
err " failed!"
|
|
else
|
|
rm "$parenttmp/$compileroutput"*
|
|
printf " done\n"
|
|
fi
|
|
}
|
|
|
|
# Function to remove old package directories and make new ones.
|
|
# For use inside a package build file.
|
|
mkandenterbuilddir() {
|
|
|
|
# Define $pkgdocs. Rest is defined in bldpkg.conf.
|
|
pkgdocs="$pkg/usr/share/doc/$app-$version"
|
|
|
|
# Remove any old pkg staging directory left by any previous build having same application name
|
|
rm -rf "$pkg"
|
|
|
|
# Now create all essential build-related directories
|
|
mkdir -p "$tmp" "$pkg/install" "$pkgdocs" "$pkgdest"
|
|
|
|
info "Entering build directory $tmp"
|
|
cd "$tmp"
|
|
}
|
|
|
|
# Function to fix permissions inside the build directory
|
|
# For use inside a package build file.
|
|
fixbuilddirpermissions() {
|
|
info "Fixing permissions in the newly-created build directory..."
|
|
chown -R root.root .
|
|
find -L . -perm /111 -a \! -perm 755 -a -exec chmod 755 {} \+ -o \
|
|
\! -perm /111 -a \! -perm 644 -a -exec chmod 644 {} \+ || true
|
|
|
|
# Place an extraction.complete file so that we know at which point to resume our build
|
|
touch ".$app-$version.extraction.complete"
|
|
}
|
|
|
|
# Function to calculate elapsed build time. runtime takes the $SECONDS variable as an argument. $SECONDS is an
|
|
# environment variable set by bash to show the number of whole seconds the shell has been running.
|
|
runtime() {
|
|
timer="$1"
|
|
|
|
local day=$(( timer / 86400 ))
|
|
local hour=$(( (timer - (day * 86400)) / 3600 ))
|
|
local mins=$(( (timer - (day * 86400) - (hour * 3600)) / 60 ))
|
|
local secs=$(( timer - (day * 86400) - (hour * 3600) - (mins * 60) ))
|
|
|
|
if [[ $day -gt 0 ]]; then
|
|
echo -n "${day}d, ${hour}h ${mins}m ${secs}s"
|
|
else
|
|
echo -n "${hour}h, ${mins}m ${secs}s"
|
|
fi
|
|
}
|
|
|
|
# Function to generate and insert sha512sums into the build file
|
|
genchecksum() {
|
|
getbuildfileanddir
|
|
|
|
# File types whose checksums will go into the new build file
|
|
info "Discarding any old sha512sums from '$buildfile'"
|
|
sed -E -i \
|
|
-e '/^sha512sums=".*"$/d' \
|
|
-e '/^sha512sums="/,/"$/d' \
|
|
-e "/^sha512sums='.*'\$/d" \
|
|
"$buildfile"
|
|
|
|
info "Adding new sha512sums in '$buildfile'"
|
|
printf 'sha512sums="\n' >> "$buildfile"
|
|
|
|
files=( *.tar.* *.zip *.t?z *.patch *.diff *.c *.h )
|
|
|
|
# For loop that searches for files matching the above extension and prints them to the bottom of the build file
|
|
for file in "${files[@]}" ; do
|
|
if [[ -f $file ]] ; then
|
|
info "Adding $file"
|
|
sha512sum "$file" >> "$buildfile"
|
|
fi
|
|
done
|
|
|
|
printf '"' >> "$buildfile"
|
|
|
|
if [[ $(find . -type d -maxdepth 1 -print0 | wc -l) -ge 1 ]]; then
|
|
warn "SHA512 checksums not generated for files inside directories!"
|
|
fi
|
|
|
|
info "You may now run 'bldpkg' again"
|
|
exit 0
|
|
}
|
|
|
|
# Function to generate and insert a runit service file into a staging directory.
|
|
# For use inside a package build file
|
|
# Usage: preprunitservice -s chrony -d -f
|
|
preprunitservice() {
|
|
unset OPTIND OPTARG
|
|
while getopts ':dfs:' option; do
|
|
case "$option" in
|
|
d) downfile=1 ;; # Add a 'down' file
|
|
f) finishfile=1 ;; # Add a 'finish' file
|
|
s) servicefile="$OPTARG" ;; # Add main runit service file
|
|
esac
|
|
done
|
|
|
|
mkdir -p $pkg/var/service
|
|
install -Dm 755 "$srcdir/$servicefile.run" "$pkg/etc/service/$servicefile/run"
|
|
(
|
|
cd $pkg
|
|
ln -sf "../../etc/service/$servicefile" "var/service/$service"
|
|
)
|
|
|
|
[[ -n $downfile ]] && touch "$pkg/etc/service/$servicefile/down"
|
|
[[ -n $finishfile ]] && touch "$pkg/etc/service/$servicefile/finish"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Function to validate symlinks.
|
|
# Usage: checkcompilersymlink symlink symlink_target
|
|
checkcompilersymlink() {
|
|
local symlinkdir="$1"
|
|
local symlinktgt="$2"
|
|
|
|
for compiler in gcc g++ cc c++ ; do
|
|
if [[ -e $symlinkdir/$compiler ]] && [[ -L $symlinkdir/$compiler ]]; then
|
|
# We use "realpath" to follow the $ccachesympath/$f symlink and act on the exit code
|
|
if [[ $(realpath -e $symlinkdir/$compiler) != $symlinktgt ]] ; then
|
|
err "'$symlinkdir/$compiler' does not point to '$symlinktgt'. Kindly fix this!"
|
|
fi
|
|
else
|
|
err "Symlink '$compiler' either does not exist or is not a symlink inside '$symlinkdir'! Kindly fix this!"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Prepare a nice HTML file that can be used to view status of the build process from a web browser
|
|
prephtmloutput() {
|
|
if [[ $htmloutput = 1 ]] ; then
|
|
if [[ $pkgstatus = 0 ]] ; then
|
|
cat << EOF >> "$parenttmp/BUILDMONITOR.html"
|
|
<tr><td><b><i><a href="/smlinux/pkgresults?pkg=$app&smver=1.0&arch=all&resultnum=25">$app $version</a></i></b></td><td>$commencedate</td><td>$finishdate</td><td>$totaltime</td><td><b style="color:#00cc00;">SUCCEEDED</b></td></tr>
|
|
EOF
|
|
elif [[ $wasinterrupted = 1 ]]; then
|
|
cat << EOF >> "$parenttmp/BUILDMONITOR.html"
|
|
<tr><td><b><i><a href="/smlinux/pkgresults?pkg=$app&smver=1.0&arch=all&resultnum=25">$app $version</a></i></b></td><td>$commencedate</td><td>$finishdate</td><td>$totaltime</td><td><b>INTERRUPTED</b></td></tr>
|
|
EOF
|
|
else
|
|
cat << EOF >> "$parenttmp/BUILDMONITOR.html"
|
|
<tr><td><b><i><a href="/smlinux/pkgresults?pkg=$app&smver=1.0&arch=all&resultnum=25">$app $version</a></i></b></td><td>$commencedate</td><td>$finishdate</td><td>$totaltime</td><td><b style="color:#ff1a1a;">FAILED</b></td></tr>
|
|
EOF
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to extract a successfully built package inside the source directory
|
|
extractinfo() {
|
|
info "Extracting package installer inside $srcdir/test..."
|
|
mkdir -p "$srcdir/test"
|
|
tar xvf "$newpkglocation" -C "$srcdir/test"
|
|
info "'$app' package installer file successfully extracted"
|
|
}
|
|
|
|
# Function to prompt a user to extract a successfully built package inside the
|
|
# the source directory
|
|
promptuser() {
|
|
# Extract package at the end of a build if autoextract is set to 1
|
|
if [[ $autoextract = 1 ]] && [[ -z $autobuildmode ]] && [[ -n $newpkglocation ]] ; then
|
|
extractinfo
|
|
fi
|
|
|
|
# Prompt the user at the end of a build whether to extract contents of a newly-built installer
|
|
# into a subdirectory called "test" inside the package source directory the build was manually initiated from.
|
|
# Has no effect on autobuilds since they are simply installed right away.
|
|
if [[ $extractprompt = 1 ]] && [[ -z $autobuildmode ]] && [[ -n $newpkglocation ]] ; then
|
|
read -r -p "[NOTIFY] Would you like the package installer of '$app-$version' to be extracted in your current directory '$srcdir' for examination? (y/N) " yn
|
|
case "$yn" in
|
|
Y|y) info "Wise Choice :-)"
|
|
extractinfo ;;
|
|
*) info "Nope? Alright " ;;
|
|
esac
|
|
fi
|
|
|
|
# Prompt the user at the end of a successful build whether to install the newly created package.
|
|
# Has no effect on autobuilds because packages there are installed automatically.
|
|
if [[ $installprompt = 1 ]] && [[ -z $autobuildmode ]] && [[ -n $newpkglocation ]] ; then
|
|
read -r -p "[NOTIFY] Would you like the package installer of '$app-$version' to be installed/upgraded onto your system? (y/N) " yn
|
|
case "$yn" in
|
|
Y|y) info "Wise choice :-) " ;
|
|
upgradepkg --install-new "$newpkglocation" ;;
|
|
*) info "Nope? Alright." ;;
|
|
esac
|
|
fi
|
|
|
|
if [[ $pkgstatus = 0 ]] ; then
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# This function will set the interrupt variable so prepbuildoutput can output the right build status
|
|
# on receiving ctrl-c from the user during a manual build.
|
|
interruptoutput() {
|
|
echo
|
|
info "Caught Keyboard Interrupt"
|
|
wasinterrupted="1"
|
|
|
|
# Restore terminal colours
|
|
echo -e "$colourd"
|
|
|
|
exit 0
|
|
}
|
|
|
|
# Function to perform post-compile tasks.
|
|
# For use inside a package build file.
|
|
mkfinalpkg() {
|
|
|
|
# Now we attempt to split the total time we'll get when making the summary into two times: compile
|
|
# time and packaging time. Here we store the value of $SECONDS variable the moment mkfinalpkg is
|
|
# invoked. We use this value as the compile time, because this is the next function that's called
|
|
# by the build script the moment a successful compile make install DESTDIR=$pkg or something similar.
|
|
|
|
# compiletimea will store the exact seconds
|
|
compiletimea="$SECONDS"
|
|
|
|
# compiletimeb will run the runtime function against compiletimea and store the resulting output
|
|
compiletimeb=$(runtime $compiletimea)
|
|
|
|
info "Leaving build directory $tmp"
|
|
info "Entering staging directory $pkg"
|
|
cd "$pkg"
|
|
info "Performing packaging tasks..."
|
|
|
|
# We don't want several directories because we are following the merge-usr principle
|
|
if [[ -z $ignorebinlib ]]; then
|
|
for directory in bin sbin lib lib64 share include usr/etc usr/sbin usr/lib64 usr/local ; do
|
|
if [[ -d $directory ]] ; then
|
|
err "$pkg/$directory is a symlink to '/usr'. Fix your build file and ensure such a directory is not created"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
info "Copying post-install files..."
|
|
|
|
[[ -e $srcdir/doinst.sh ]] && cp "$srcdir/doinst.sh" "install/"
|
|
|
|
# If usr/share/applications directory exists but there is no doinst.sh in the source directory, create one using cat
|
|
if [[ -d usr/share/applications ]] && [[ ! -e $srcdir/doinst.sh ]] ; then
|
|
info "Found /usr/share/applications but couldn't find any doinst.sh in the source directory.
|
|
Creating one automatically that refreshes GTK cache."
|
|
cat << EOF >> "install/doinst.sh"
|
|
[[ -x /etc/rc.d/rc.gtk ]] && /etc/rc.d/rc.gtk
|
|
EOF
|
|
|
|
# Or if usr/share/applications directory exists, and there is a doinst.sh file in the source directory, but there is no mention of rc.gtk, then too create one using cat
|
|
elif [[ -d usr/share/applications ]] && [[ -e $srcdir/doinst.sh ]] && ! grep -q 'rc.gtk' "$srcdir/doinst.sh" ; then
|
|
info "Found usr/share/applications but couldn't find any rc.gtk lines inside doinst.sh in the source directory.
|
|
Creating one automatically that refreshes GTK cache."
|
|
cat << EOF >> "install/doinst.sh"
|
|
[[ -x /etc/rc.d/rc.gtk ]] && /etc/rc.d/rc.gtk
|
|
EOF
|
|
fi
|
|
|
|
# Compress and link manpages
|
|
if [[ -d usr/share/man ]]; then
|
|
info "Compressing and linking man pages..."
|
|
(
|
|
cd "usr/share/man"
|
|
for manpagedir in $(find . -type d -name "man*") ; do
|
|
(
|
|
cd $manpagedir
|
|
for eachpage in $( find . -type l -maxdepth 1) ; do
|
|
ln -s $( readlink $eachpage ).gz $eachpage.gz
|
|
rm $eachpage
|
|
done
|
|
gzip -9 ./*.? >/dev/null 2>&1 || true
|
|
)
|
|
done
|
|
)
|
|
fi
|
|
|
|
# Provide a copy of the package build file as a source of reference for users
|
|
install -Dm 644 "$srcdir/$buildfile" "$pkgdocs/smbuild"
|
|
|
|
# Remove libtool archive files
|
|
if [[ "$(findlibtoolfiles | wc -l)" -ge 1 ]] ; then
|
|
info "Discarding libtool archive (.la) files..."
|
|
findlibtoolfiles -exec rm -v {} \;
|
|
fi
|
|
|
|
# If $preservestaticlibs is not set in the package build file, delete all static libraries
|
|
if [[ -z $preservestaticlibs ]] ; then
|
|
if [[ "$(findarchivefiles | wc -l)" -ge 1 ]] ; then
|
|
info "Discarding static libraries..."
|
|
findarchivefiles | xargs rm -v
|
|
fi
|
|
fi
|
|
|
|
# Now strip the binaries and libraries.
|
|
if [[ -z $debug ]] ; then
|
|
if [[ "$(findelffiles | wc -l)" -ge 1 ]] ; then
|
|
info "Stripping ELF files..."
|
|
# scanelf will, if a static archive is found, return object files inside that archive, so play safe
|
|
findelffiles | cut -d: -f1 | uniq | xargs strip --strip-unneeded
|
|
fi
|
|
fi
|
|
|
|
# Move package documentation and licenses into $pkgdocs
|
|
if [[ -d usr/share/doc/$app ]] ; then
|
|
cp -ar usr/share/doc/$app $pkgdocs/
|
|
rm -r usr/share/doc/$app
|
|
fi
|
|
|
|
# Calculate total files, directories, symlinks and uncompressed staging directory size
|
|
if [[ $showsummary = 1 ]] ; then
|
|
totalfilecount=$(find "$pkg" -type f | wc -l)
|
|
totaldircount=$(find "$pkg" -type d | wc -l)
|
|
totalsymcount=$(find "$pkg" -type l | wc -l)
|
|
packusize1=$(du -s "$pkg" | awk '{print $1}')
|
|
|
|
# Here we ascertain the packaging time taken to actually prepare the final package. For this, we
|
|
# must reset the SECONDS variable to ensure accuracy
|
|
SECONDS=0
|
|
fi
|
|
|
|
# Store package location inside this variable:
|
|
newpkglocation="$pkgdest/$app-$version-$arch-$build.$pkgext"
|
|
|
|
# https://gist.github.com/ruario/9672717
|
|
# Create the SMLinux package
|
|
info "Generating SMLinux package..."
|
|
|
|
# If $disablepkgsymlinks is not set in the package build file, change any symlinks into shell script code. An example is base/initfs wherein
|
|
# we only want the doinst.sh file in the source directory to be copied, not manipulated.
|
|
|
|
if [[ -z $disablepkgsymlinks ]] ; then
|
|
if find . -type l | grep -qm1 .; then
|
|
info "Found symlinks, preparing install/doinst.sh..."
|
|
find . -type l -printf '( cd %h ; rm -rf %f )\n( cd %h ; ln -sf %l %f )\n' -delete > install/symlinks
|
|
[[ -f install/doinst.sh ]] && printf '\n' | cat - install/doinst.sh >> install/symlinks
|
|
mv install/symlinks install/doinst.sh
|
|
fi
|
|
fi
|
|
|
|
# Discard charset.alias
|
|
find "$pkg" -name "charset.alias" -exec rm -v {} \;
|
|
|
|
# Also discard the lib and install directory if it's empty but don't error out
|
|
if [[ -z ignorebinlib ]] ; then
|
|
for dir in lib usr/lib usr/share usr/lib64 install ; do
|
|
rmdir $dir &> /dev/null || true
|
|
done
|
|
fi
|
|
|
|
tar cvf - . --format gnu \
|
|
--xform 'sx^\./\(.\)x\1x' \
|
|
--show-stored-names | "$compressor" "$compressoropts" > "$newpkglocation"
|
|
pkgstatus=$?
|
|
|
|
echo
|
|
info "SMLinux package '$app-$version-$arch-$build.$pkgext' successfully generated in $pkgdest."
|
|
|
|
info "Leaving staging directory $pkg"
|
|
|
|
# With SECONDS reset, the shell will add in a fresh value, which we can now use to ascertain
|
|
# the packaging time, by again passing that value as an argument to the runtime function
|
|
if [[ $showsummary = 1 ]] || [[ $htmloutput = 1 ]] ; then
|
|
packagetimea="$SECONDS"
|
|
packagetimeb=$(runtime "$packagetimea")
|
|
fi
|
|
|
|
|
|
if [[ $showsummary = 1 ]]; then
|
|
# Determine size of srcdir aka source directory
|
|
srcdirsize=$(du -s "$srcdir" | awk '{print $1}')
|
|
# Determine size of tmp aka build directory size
|
|
builddirsize=$(du -s "$tmp" | awk '{print $1}')
|
|
|
|
# Calculate SSD write savings if TMPFS has been used
|
|
if [[ $tmpfsenabledforthispackage = 1 ]] ; then
|
|
|
|
# Determine size of staging directory
|
|
pkgdirsize=$(du -s "$pkg" | awk '{print $1}')
|
|
|
|
# Sum total of the above two variables is the amount of writes we saved
|
|
tmpfssavingssum0="$((builddirsize + pkgdirsize))"
|
|
|
|
# We'll get sum in kB. Convert that to MB.
|
|
tmpfssavingssum1=$(echo "scale=2 ; "$tmpfssavingssum0" / 1024" | bc)
|
|
|
|
# Store the result for build summary to pickup.
|
|
tmpfssavingsize="and $tmpfssavingssum1"MB" writes to SSD saved"
|
|
fi
|
|
fi
|
|
|
|
cd "$srcdir"
|
|
|
|
# Delete the build directory if preservebuilddir is set to 0
|
|
[[ $preservebuilddir = 0 ]] && rm -rf "$tmp"
|
|
|
|
# Delete the package staging directory if preservepackagedir is set to 0
|
|
[[ $preservepackagedir = 0 ]] && rm -rf "$pkg"
|
|
|
|
if [[ $pkgstatus = 0 ]] ; then
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
findarchivefiles() {
|
|
find "$pkg" -type f -name "*.a"
|
|
}
|
|
|
|
findlibtoolfiles() {
|
|
find "$pkg" -type f -name "*.la" "$@"
|
|
}
|
|
|
|
findelffiles() {
|
|
# Only scan for static archives if preservestaticlibs is set to 1
|
|
[[ $preservestaticlibs = "1" ]] && staticelfflag=",ET_REL"
|
|
scanelf --archives --nobanner --recursive --symlink --format "%F" --etype "ET_EXEC,ET_DYN$staticelfflag" .
|
|
}
|
|
|
|
buildfilecleanup() {
|
|
# Discard all temporary files
|
|
rm -f "$parenttmp/BUILDING"
|
|
}
|
|
|
|
prepbuildoutput() {
|
|
# Start of the showsummary if/else check
|
|
if [[ $showsummary = 1 ]]; then
|
|
|
|
# Get the build completion time and store it in a variable
|
|
finishdate=$(date '+%a, %d %b %Y, %T')
|
|
|
|
# If compiletimea is set, then do a sum total of compiletimea and packagetimea variables to get the
|
|
# total time in seconds and use that as an argument for the runtime function. If compiletimea is not set,
|
|
# invoke the runtime function alone on new reset $SECONDS
|
|
if [[ -n $compiletimea ]] && [[ $pkgstatus = 0 ]] ; then
|
|
finalcompiletime=$((compiletimea + packagetimea))
|
|
totaltime=$(runtime $finalcompiletime)
|
|
else
|
|
totaltime=$(runtime $SECONDS)
|
|
fi
|
|
|
|
# Stick to 8/16 colours, those are supported on most terminals
|
|
if [[ $colours = 1 ]]; then
|
|
colourscheck=$(tput colors 2>/dev/null)
|
|
if [[ $? = 0 ]] && [[ $colourscheck -gt 2 ]] ; then
|
|
# Red when the build fails
|
|
colourr=$(printf '\e[41m')
|
|
# Yellow when the build is interrupted
|
|
coloury=$(printf '\e[93m')
|
|
# Green when the build succeeds
|
|
colourg=$(printf '\e[92m')
|
|
# Cyan for the short questions
|
|
colourc=$(printf '\e[96m')
|
|
# App name/version colours
|
|
colourv=$(printf '\e[92m')
|
|
# Restore to default
|
|
colourd=$(printf '\e[0m')
|
|
fi
|
|
fi
|
|
|
|
# Determine the build type
|
|
if [[ -n $autobuildmode ]]; then
|
|
# We are using the autobuild system
|
|
buildsys="Autobuild"
|
|
else
|
|
# We compiled the package manually
|
|
buildsys="Manual"
|
|
fi
|
|
|
|
# Determine if distcc was used. If $distcc set to 1, set distccstats in the build summary
|
|
if [[ $distcc = 1 ]] ; then
|
|
# If distcc was used, cut out --randomize and output rest of the DISTCC_HOSTS variable
|
|
distccvar=$(echo "$DISTCC_HOSTS" | sed 's@--randomize@@')
|
|
distccstats="Yes, $distccvar"
|
|
# Else if distcc is unset, set distccstats in the build summary
|
|
elif [[ -z $distcc ]] || [[ $distcc = 0 ]]; then
|
|
distccstats="No, disabled"
|
|
fi
|
|
|
|
|
|
# Determine if ccache was used. If we are compiling inside tmpfs, we are not using ccache at all, so set
|
|
# ccachestats accordingly in the build summary
|
|
if [[ $ccache = 1 ]] && [[ $tmpfsenabledforthispackage = 1 ]] ; then
|
|
ccachestats="Set to 1 but disabled due to tmpfs"
|
|
|
|
elif [[ $ccache = 1 ]] && [[ $tmpfs = 0 ]] ; then
|
|
ccacheusedsize=$(ccache -s | grep "cache size" | head -n 1 | awk '{ $1=$2="" ; print $0}' | sed 's@ @@g')
|
|
ccachetotalsize=$(ccache -s | grep "max cache size" | awk '{ $1=$2=$3="" ; print $0}' | sed 's@ @@g')
|
|
ccachestats="Yes, $ccacheusedsize / $ccachetotalsize Allocated"
|
|
|
|
elif [[ $ccache = 1 ]] && [[ -z $tmpfs ]] ; then
|
|
ccachestats="Yes"
|
|
|
|
elif [[ -z $ccache ]] || [[ $ccache = 0 ]] ; then
|
|
ccachestats="No, disabled in bldpkg.conf"
|
|
fi
|
|
|
|
# Determine the build type
|
|
if [[ $debug = 1 ]] ; then
|
|
bldtype="*DEBUG* build"
|
|
else
|
|
bldtype="General build, no debug symbols"
|
|
fi
|
|
|
|
# Determine whether tmpfs was used
|
|
if [[ $tmpfsenabledforthispackage = 1 ]] ; then
|
|
tmpfsstate="Yes"
|
|
|
|
elif [[ $tmpfs = 1 ]] && [[ $tmpfsenabledforthispackage = 0 ]]; then
|
|
tmpfsstate="*Not for this package* but enabled in bldpkg.conf"
|
|
|
|
elif [[ $tmpfs = 1 ]] && [[ $tmpfscheckfailed = 1 ]]; then
|
|
tmpfsstate="*NOPE, TMPFS DIRECTORY CHECK FAILED* but enabled in bldpkg.conf"
|
|
|
|
else
|
|
tmpfsstate="No, disabled in bldpkg.conf"
|
|
fi
|
|
|
|
# Determine if the build was successful or not
|
|
if [[ $pkgstatus = 0 ]] ; then
|
|
# Determine the compressed size
|
|
packsize=$(du -bk "$newpkglocation" | awk '{print $1}')
|
|
|
|
# Determine the uncompressed size
|
|
packusize="$packusize1"
|
|
|
|
# If the package was built successfully, output the installer sizes
|
|
# Space saving = 1 - Compressed Size / Uncompressed size.
|
|
# Also, bc code taken from:
|
|
# https://stackoverflow.com/questions/56945130/bash-echo-percentage-with-no-decimal-point-with-result-returned-from-bc-comman
|
|
compressedsize=$(echo $(echo "scale=2 ; 1 - "$packsize" / "$packusize"" | bc ) | sed 's@.@@')
|
|
|
|
bldstatus="$colourg Successful! :-D $colourd
|
|
Srce Size: $colourd Compressed: $srcdirsize"K", Uncompressed: $builddirsize"K"
|
|
Pkg Size: $colourd Uncompressed: $packusize"K", Compressed: $packsize"K" ("$compressedsize'%'")
|
|
Pkg Has: $colourd $totalfilecount files and $totalsymcount symlinks in $totaldircount dirs"
|
|
|
|
elif [[ $wasinterrupted = 1 ]]; then
|
|
bldstatus="$coloury ** INTERRUPTED ** :-/ $colourd"
|
|
else
|
|
bldstatus="$colourr !! FAILED !! :-( $colourd"
|
|
fi
|
|
|
|
# Finally prepare the summary
|
|
|
|
# If we have $compiletimeb set, then assume the compile went well and output compile and packaging times inside a variable
|
|
if [[ -n $totaltime ]] && [[ -z $packagetimeb ]]; then
|
|
ttime="$totaltime"
|
|
elif [[ -n $totaltime ]] && [[ -n $packagetimeb ]]; then
|
|
ttime="$totaltime ( $compiletimeb Compile ) + ( $packagetimeb Packaging )"
|
|
fi
|
|
|
|
[[ -z $section ]] && section="None"
|
|
|
|
# Output the build summary to the user on every build
|
|
cat << EOF
|
|
$colourc ------------------------------------------------------------------------------- $colourd
|
|
$colourc BUILD SUMMARY FOR PACKAGE $colourv'$app'$colourc VERSION $colourv'$version'$colourc TAG $colourv'$build'
|
|
$colourc ------------------------------------------------------------------------------- $colourd
|
|
$colourc Build Status: $colourd $bldstatus
|
|
$colourc Build Section: $colourd $section
|
|
$colourc Total Time: $colourd $ttime
|
|
$colourc Started: $colourd $commencedate
|
|
$colourc Stopped: $colourd $finishdate
|
|
$colourc Ccache Used? $colourd $ccachestats
|
|
$colourc Distcc Used? $colourd $distccstats
|
|
$colourc TMPFS Used? $colourd $tmpfsstate $tmpfssavingsize
|
|
$colourc MAKEFLAGS: $colourd $MAKEFLAGS
|
|
$colourc CFLAGS Used: $colourd $CFLAGS
|
|
$colourc Compressor: $colourd $compressor ($compressoropts)
|
|
$colourc Build Type: $colourd $buildsys & $bldtype
|
|
$colourc ------------------------------------------------------------------------------- $colourd
|
|
EOF
|
|
|
|
fi
|
|
# Completion of the showsummary if/else check
|
|
|
|
# Discard temporary files
|
|
buildfilecleanup
|
|
|
|
# Show HTML output if enabled
|
|
prephtmloutput
|
|
|
|
# Prompt user for extracting/installing the package if the build has succeeded
|
|
promptuser
|
|
}
|
|
|
|
stage0prep() {
|
|
# Check if $parenttmp is set and is a directory and can be written into
|
|
if [[ -z $parenttmp ]] ; then
|
|
err "parenttmp variable not set in bldpkg.conf."
|
|
elif [[ ! -d $parenttmp ]] ; then
|
|
err "parenttmp variable set to '$parenttmp' in bldpkg.conf is not a directory."
|
|
elif ! touch "$parenttmp/.parenttmpwritetest" ; then
|
|
err "Parent temp directory '$parenttmp' is not writable!"
|
|
else
|
|
# Discard the test file
|
|
rm "$parenttmp/.parenttmpwritetest"
|
|
fi
|
|
|
|
printf "[INFO] Validating compressor '$compressor'..."
|
|
mkdir -p "$parenttmp/.tar.testdir"
|
|
touch "$parenttmp/.tar.testdir/.tar.testfile"
|
|
|
|
if ! tar -cf - -C "$parenttmp/.tar.testdir" . | "$compressor" "$compressoropts" > "$parenttmp/.tar.testdir.$pkgext" ; then
|
|
err "Failed to validate '$compressor' options! Please check your bldpkg.conf"
|
|
fi
|
|
|
|
printf " done\n"
|
|
rm -r "$parenttmp/.tar.testdir"*
|
|
|
|
# Validate the TMPFS directory if usetmpfs is set to 1 and tmpfsdir variable is set. If it fails,
|
|
# declare a variable for the build summary.
|
|
if [[ $tmpfs = 1 ]] && [[ -n $tmpfsdir ]]; then
|
|
if [[ ! -d $tmpfsdir ]] || ! touch "$tmpfsdir/.tmpfswritetest" \
|
|
|| [[ $(findmnt -no TARGET $tmpfsdir) != $tmpfsdir ]] \
|
|
|| [[ $(findmnt -no FSTYPE $tmpfsdir) != tmpfs ]]; then
|
|
tmpfscheckfailed=1
|
|
fi
|
|
|
|
# Discard the file used to test the tmp directory
|
|
rm "$tmpfsdir/.tmpfswritetest"
|
|
|
|
# Check the tmpfsdir for stale directories from previous pkg builds. If found, issue a warning.
|
|
if [[ $(find "$tmpfsdir" -type d -maxdepth 1 -name "*.src" -o -name "package-*" | wc -l) -gt 0 ]]; then
|
|
if [[ ! -d $tmpfsdir/package-$app ]] || [[ ! -d $tmpfsdir/$app.src ]] ; then
|
|
warn "TMPFS directory '$tmpfsdir' has stale directories from previous builds!"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Validate system swap if swapcheck is defined and set to 1
|
|
if [[ $swapcheck = 1 ]]; then
|
|
if inarray "${app}" "${packagesrequiringswap[@]}" ; then
|
|
|
|
# Here we determine available system swap size needed to compile exceptional packages that pull
|
|
# in a lot of RAM. Those packages are listed under the packagesrequiringswap array in bldpkg.conf.
|
|
# Check whether swap is available on the system and if it is, determine its size. If its size
|
|
# is >= swapsize, we are all good. If it's less than swapsize, we exit with a status 1.
|
|
|
|
swapcheck=$(grep "SwapFree" /proc/meminfo | awk '{print $2}')
|
|
[[ $swapcheck -lt $swapsize ]] && err "Insufficient swap size to build packages, advise increasing it."
|
|
fi
|
|
fi
|
|
|
|
# Set the build and package staging directories. This is where package files that get "installed" go into,
|
|
# for example 'make install DESTDIR=$pkg' or 'DESTDIR="$pkg" ninja install'.
|
|
|
|
# If tmpfs is set to 1 and tmpfscheckfailed variable is unset, determine
|
|
# if the $app is in the exception list and whether to build inside or outside the TMPFS directory.
|
|
if [[ $tmpfs = 1 ]] && [[ -z $tmpfscheckfailed ]] ; then
|
|
|
|
# If $app is in the TMPFS exception list inside bldpkg.conf, compile it *OUTSIDE* the TMPFS
|
|
# directory, i.e the non-TMPFS directory, else compile it *INSIDE* the TMPFS directory. This if/else
|
|
# is solely for deciding whether $app is in the exception list or not.
|
|
if inarray "${app}" "${tmpfsexceptionlist[@]}" ; then
|
|
|
|
info "'$app' is in tmpfs exception list. Falling back to non-tmpfs directory"
|
|
|
|
# We DO NOT compile inside tmpfsdir
|
|
tmpfsenabledforthispackage=0
|
|
|
|
# In the absence of tmpfs, we use the normal directory
|
|
tmp="$nontmpfsdir/$app.src"
|
|
pkg="$nontmpfsdir/package-$app"
|
|
else
|
|
# We compile inside tmpfsdir. Set the tmpfsenabledforthispackage variable here to inform build
|
|
# summary function at the bottom
|
|
tmpfsenabledforthispackage=1
|
|
|
|
# Disable ccache
|
|
ccache=0
|
|
|
|
# Override preservebuilddir and preservepackagedir to remove both build and package staging directories
|
|
preservebuilddir=0
|
|
preservepackagedir=0
|
|
|
|
# Get the directory from the tmpfsdir variable for extracting the source and set it as our build
|
|
# and staging directory
|
|
tmp="$tmpfsdir/$app.src"
|
|
pkg="$tmpfsdir/package-$app"
|
|
|
|
fi
|
|
else
|
|
# If tmpfs is disabled, we compile in the non-TMPFS directory
|
|
tmp="$nontmpfsdir/$app.src"
|
|
pkg="$nontmpfsdir/package-$app"
|
|
fi
|
|
|
|
if inarray "${pkg}" "${protecteddirectories[@]}" ; then
|
|
echo "############ ATTENTION ############"
|
|
err "'pkg' VARIABLE IS SET TO '$pkg' WHICH IS A PROTECTED DIRECTORY! EXITING!"
|
|
fi
|
|
|
|
# Also check tmp directory in advance
|
|
if inarray "${tmp}" "${protecteddirectories[@]}" ; then
|
|
echo "############ ATTENTION ############"
|
|
err "'tmp' VARIABLE IS SET TO '$tmp' WHICH IS A PROTECTED DIRECTORY! EXITING!"
|
|
fi
|
|
|
|
# Run a for loop to find compile and build-related files
|
|
for requiredfile in "${rqfiles[@]}"; do
|
|
if [[ ! -x $(type -p "$requiredfile") ]] ; then
|
|
err "Could not find required program '$requiredfile'!"
|
|
fi
|
|
done
|
|
}
|
|
|
|
stage1prep() {
|
|
# Apply CPU-specific compiler variables defined inside bldpkg.conf
|
|
# noarch is set inside initfs, pkgtools, GTK themes and some other stuff.
|
|
|
|
# Only set $arch if it's not set in the build file. We take the help of $HOSTTYPE variable set by the bash shell.
|
|
[[ -z $arch ]] && arch="$HOSTTYPE"
|
|
|
|
for compilervariable in CC CXX CFLAGS ; do
|
|
[[ -z $compilervariable ]] && err "$compilervariable not set in bldpkg.conf!"
|
|
done
|
|
|
|
# Clean out CFLAGS if $arch is set to noarch
|
|
if [[ $arch = noarch ]]; then
|
|
CFLAGS=""
|
|
elif [[ -n $debug ]]; then
|
|
CFLAGS="$gccdebug $CFLAGS"
|
|
fi
|
|
|
|
CXXFLAGS="$CFLAGS"
|
|
export CC CXX CFLAGS CXXFLAGS MAKEFLAGS
|
|
|
|
# Validate available compilers. Quotes are important while passing C/CXXFLAGS
|
|
compilertestfile "$CC" "$CFLAGS" cc-test.c cc-test
|
|
compilertestfile "$CXX" "$CXXFLAGS" cxx-test.cpp cxx-test
|
|
|
|
# Validate everything related to ccache if ccache is set
|
|
if [[ $ccache = 1 ]]; then
|
|
[[ ! -x $ccachebinpath ]] && err "Ccache binary does not exist/is not an executable!"
|
|
|
|
checkcompilersymlink $ccachesymdirpath $ccachebinpath
|
|
|
|
# Prepare $PATH and export it only if ccache is unset in the pkg build file
|
|
PATH="$ccachesymdirpath:$PATH"
|
|
export PATH
|
|
fi
|
|
|
|
# Validate everything related to distcc if distcc is set
|
|
if [[ $distcc = 1 ]] ; then
|
|
# Check if distcc exists and is an executable
|
|
[[ ! -x $distccbinpath ]] && err "Distcc binary was either not found or is not an executable"
|
|
|
|
checkcompilersymlink $distccsymdirpath $distccbinpath
|
|
|
|
# Prepare $PATH and export DISTCC_HOSTS and DISTCC_IO_TIMEOUT only if distcc is unset in the pkg build file
|
|
# netcat hosts inside $DISTCC_HOSTS by checking for an open port
|
|
info "Validating distcc hosts..."
|
|
# Check if we have nc
|
|
if [[ ! -x /bin/nc ]] ; then
|
|
warn "nc does not exist! Ignoring this..."
|
|
else
|
|
# Remove the common options along with the slash and the numbers after it
|
|
hosts=$(echo "$DISTCC_HOSTS" | sed -e 's@/[a-z0-9,]*@@g' -e 's@--randomize@@' -e 's@localhost@@')
|
|
for host in ${hosts[@]} ; do
|
|
# We only run distccd on TCP port 3632
|
|
if ! /bin/nc -z -w 1 "$host" 3632 > /dev/null 2>&1 ; then
|
|
warn "Distcc host '$host' is OFFLINE! Rewriting DISTCC_HOSTS"
|
|
DISTCC_HOSTS=$(echo "$DISTCC_HOSTS" | sed "s@$host/[a-z0-9,]*@@")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
PATH="$distccsymdirpath:$PATH"
|
|
export DISTCC_HOSTS DISTCC_IO_TIMEOUT PATH
|
|
fi
|
|
|
|
# If verbosebuild is set in bldpkg.conf or if verbosity is set to 1 from getopts, set V and
|
|
# VERBOSE environment variables for make and cmake build systems to pick up.
|
|
if [[ -z $disableverbosebuild ]] ; then
|
|
if [[ $verbosebuild = 1 ]] || [[ $getoptsverbosebuild = 1 ]] ; then
|
|
V=1
|
|
VERBOSE=1
|
|
export V VERBOSE
|
|
fi
|
|
fi
|
|
|
|
# If $noautoconfsite is unset in an individual package build file, export CONFIG_SITE variable into the build
|
|
# environment for a package's configure script to pickup. Most autoconf-compatible configure scripts will
|
|
# automatically pick up this variable from the environment and speed up the initial configure process.
|
|
if [[ -z $noautoconfsite ]] ; then
|
|
if [[ -n $configsite ]] && [[ -e $configsite ]]; then
|
|
CONFIG_SITE="$configsite"
|
|
export CONFIG_SITE
|
|
fi
|
|
fi
|
|
|
|
# Do a preliminary package dependency check if $checkdependencies is set to 1 in bldpkg.conf
|
|
if [[ $checkdependencies = 1 ]] ; then
|
|
info "Parsing $app 's dependency list..."
|
|
for packagedep in $requires; do
|
|
depcount=$(find /usr/share/doc -type f -name "smbuild" | wc -l)
|
|
# If count is 1, we are ok
|
|
if [[ $depcount = 1 ]] ; then
|
|
info "Found dependency '$packagedep'"
|
|
# If count is 0, we exit, because we are in trouble
|
|
elif [[ $depcount = 0 ]] ; then
|
|
err "Did not find dependency '$packagedep'"
|
|
# If count is greater than or equal to 2, we are in slightly less trouble
|
|
elif [[ $depcount -ge 2 ]] ; then
|
|
warn "Found multiple versions of '$packagedep' !"
|
|
sleep 0.5
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
autobuild() {
|
|
trap "interruptoutput" INT
|
|
|
|
# We don't need colours and we set arch on purpose so that the packages land in the right directory
|
|
colours=0
|
|
arch="$HOSTTYPE"
|
|
|
|
# For loop to recurse through sections defined inside bldpkg.conf
|
|
for section in "${sections[@]}" ; do
|
|
|
|
logdir="$parenttmp/sml/sml-buildlogs/$arch/$section"
|
|
mkdir -p $logdir
|
|
|
|
# If the defined section does not exist, abort
|
|
if [[ ! -d $section ]] ; then
|
|
err "Section directory '$section' not found!"
|
|
elif [[ ! -f "$section/.buildlist.$section" ]] ; then
|
|
err "Section build list file '$section/.buildlist.$section' not found!"
|
|
fi
|
|
|
|
cd $section
|
|
|
|
# This check compares a list of source directories with the list of the
|
|
# packages in the build list and warns of any missing package names
|
|
# in either of the two.
|
|
|
|
dirtempfile=$(mktemp $parenttmp/DIRECTORYNAMES."$section".XXXXXX)
|
|
dirfiletemppath="$dirtempfile"
|
|
dirlist=$(find . -type d -maxdepth 1 -mindepth 1 | sed 's@./@@' | sort > $dirfiletemppath)
|
|
|
|
packtempfile=$(mktemp $parenttmp/BUILDFILENAMES."$section".XXXXXX)
|
|
packfiletemppath="$packtempfile"
|
|
sort .buildlist.$section > $packfiletemppath
|
|
|
|
directorycount="$( wc -l < $dirtempfile )"
|
|
buildlistcount="$( wc -l < $packtempfile )"
|
|
|
|
# Get number of total packages
|
|
totalpkgnumber="$(wc -l < .buildlist.$section)"
|
|
|
|
if diff -u "$dirfiletemppath" "$packfiletemppath" > /dev/null 2>&1 ; then
|
|
diffstatus="0"
|
|
else
|
|
diffstatus="1"
|
|
fi
|
|
|
|
if [[ "$diffstatus" != "0" ]]; then
|
|
warn "In section '"$section"', the number of packages in the hidden file '.buildlist."$section"' is different to the number of package directories. Some packages may not have been added to this file/section directory. They are listed below:"
|
|
|
|
diff -u "$dirfiletemppath" "$packfiletemppath" || true
|
|
diff -u "$packfiletemppath" "$dirfiletemppath" || true
|
|
|
|
warn "Building anyways :-) "
|
|
sleep 2
|
|
fi
|
|
rm -f $packfiletemppath $dirfiletemppath
|
|
|
|
while IFS="" read -r package || [[ -n $package ]] ; do
|
|
(
|
|
SECONDS=0
|
|
pkgdest="$pkgdest/$arch/$section"
|
|
currentpkgnumber="$(grep -Ewn "^$package" .buildlist.$section | cut -d: -f 1)"
|
|
|
|
# We need this variable to switch back to the section directory inside the loop
|
|
startdate
|
|
currentpwd="$PWD"
|
|
|
|
cd $package
|
|
|
|
# Get the name of the buildfile and validate it
|
|
getbuildfileanddir
|
|
validatebldfile
|
|
|
|
# Check if package installer exists or is installed on the system
|
|
# If the package installer does not exist in the set pkg destination, build it
|
|
if [ ! -f $pkgdest/$app-$version-*-$build.* ] ; then
|
|
info "Building package '$app' version '$version' ..."
|
|
htmloutput
|
|
stage0prep
|
|
verifychecksums
|
|
prepbuilddir
|
|
stage1prep
|
|
|
|
trap "prepbuildoutput" EXIT
|
|
build
|
|
|
|
# If the package is not installed in the system, install it
|
|
if [ ! -f /var/log/packages/$app-$version-*-$build ] ; then
|
|
upgradepkg --install-new $pkgdest/$app-$version-*-$build.*
|
|
else
|
|
info "'$app-$version' already installed, skipping it"
|
|
fi
|
|
elif [ ! -f /var/log/packages/$app-$version-*-$build ] ; then
|
|
upgradepkg --install-new $pkgdest/$app-$version-*-$build.*
|
|
else
|
|
info "Already built '$app-$version', skipping it"
|
|
fi
|
|
|
|
cd $currentpwd
|
|
) </dev/null
|
|
done < ".buildlist.$section"
|
|
|
|
cd ..
|
|
|
|
done
|
|
}
|
|
|
|
manualbuild() {
|
|
startdate
|
|
trap "interruptoutput" INT
|
|
|
|
validatebldfile
|
|
|
|
# Display the package and its version we are building or resuming
|
|
if [[ -z $resumepkgbuild ]] ; then
|
|
info "Building package '$app' version '$version' ..."
|
|
else
|
|
info "Resuming build of package '$app' version '$version' ..."
|
|
fi
|
|
|
|
htmloutput
|
|
stage0prep
|
|
|
|
# If $tmp exists, get the path to the $.app-$version.extraction.complete file
|
|
[[ -d $tmp ]] && buildresumepath="$(find $tmp -type f -name .$app-$version.extraction.complete)"
|
|
|
|
# Prompt the user
|
|
if [[ -f $buildresumepath ]] && [[ -z $resumepkgbuild ]] && [[ $autoresumepkgbuild = 1 ]] ; then
|
|
read -r -p "[NOTIFY] Would you like to resume building? " yn
|
|
case "$yn" in
|
|
N|n|No|no) info "Nope? Alright." ;
|
|
unset resumepkgbuild ;;
|
|
*) info "Wise choice :-) "
|
|
resumepkgbuild=1 ;;
|
|
esac
|
|
fi
|
|
|
|
verifychecksums
|
|
# If $resumepkgbuild variable is not set in getopts, only then execute prepbuilddir variable
|
|
if [[ -z $resumepkgbuild ]] ; then
|
|
prepbuilddir
|
|
else
|
|
mkandenterbuilddir
|
|
# fixbuilddirpermissions places a file ".$app-$version.extraction.complete". Only resume the build if that file exists
|
|
if [[ ! -f $buildresumepath ]] ; then
|
|
err "Can't resume build of '"$app"'! Are you certain the source was extracted completely?"
|
|
else
|
|
cd ${buildresumepath%/*}
|
|
fi
|
|
fi
|
|
|
|
stage1prep
|
|
trap "prepbuildoutput" EXIT
|
|
build
|
|
}
|
|
|
|
# Source the main configuration file holding all necessary values
|
|
if [[ -f $HOME/.bldpkg.conf ]] ; then
|
|
source "$HOME/.bldpkg.conf"
|
|
elif [[ -f /etc/bldpkg.conf ]] ; then
|
|
source /etc/bldpkg.conf
|
|
else
|
|
err "Bldpkg configuration file '/etc/bldpkg.conf' not found!"
|
|
fi
|
|
|
|
# While loop for providing handy arguments to users. Some will override bldpkg.conf.
|
|
while getopts ':adef:ghj:rstvx' option; do
|
|
case "$option" in
|
|
a) autobuildmode=1 ;;
|
|
d) debug=1 ;; # Produce a debug build with -g3
|
|
e) extractprompt=0;
|
|
autoextract=1 ;; # Automatically extract the final pkg installer inside user's PWD
|
|
f) setbuildfile="$OPTARG" ;;
|
|
g) genchecksum=1 ;
|
|
[[ -z $autobuildmode ]] && genchecksum ;;
|
|
h) help ;;
|
|
j) custommakeflags="$OPTARG" ;;
|
|
r) resumepkgbuild=1 ;;
|
|
s) showsummary=1 ;; # Show build summary at the end of the build irrespective of the build status
|
|
t) tmpfs=1 ;;
|
|
v) getoptsverbosebuild=1 ;;
|
|
x) set -x ;; # Invoke bash's -x option for command tracing
|
|
*) help ;;
|
|
esac
|
|
done
|
|
|
|
# The getopts builtin sets the OPTIND environment variable whose value is set to 1 if no argument is given.
|
|
# For every argument passed, the number is incremented by 1. In our case, if OPTIND equals 1, no argument
|
|
# was passed. We therefore expect a package build file to be present which will then be validated and sourced.
|
|
if [[ $OPTIND = 1 ]] ; then
|
|
getbuildfileanddir
|
|
if [[ ! -f $buildfile ]] ; then
|
|
nopackagebuildfileerror
|
|
elif ! validatebldfile ; then
|
|
err "'$buildfile' build file validation failed!"
|
|
else
|
|
manualbuild
|
|
fi
|
|
|
|
# If OPTIND is greater than 1, perform various checks and actions
|
|
elif [[ $OPTIND -gt 1 ]] ; then
|
|
|
|
# If MAKEFLAGS and custommakeflags is set, let custommakeflags take precedence
|
|
if [[ -n $custommakeflags ]] ; then
|
|
validatemakeflags $custommakeflags
|
|
MAKEFLAGS="-j$custommakeflags"
|
|
|
|
elif [[ -n $MAKEFLAGS ]] ; then
|
|
validatemakeflags $MAKEFLAGS
|
|
|
|
elif [[ -z $MAKEFLAGS ]] ; then
|
|
# Or fetch the number from nproc
|
|
MAKEFLAGS="-j$(nproc --all)"
|
|
fi
|
|
|
|
if [[ $autobuildmode = 1 ]] ; then
|
|
# Disallow certain variables that may be passed on by getopts
|
|
if [[ -n $autoextract ]] ; then
|
|
err "-e option not allowed in autobuild mode"
|
|
elif [[ -n $setbuildfile ]] ; then
|
|
err "-f option not allowed in autobuild mode"
|
|
elif [[ -n $genchecksum ]] ; then
|
|
err "-g option not allowed in autobuild mode"
|
|
elif [[ -n $resumepkgbuild ]] ; then
|
|
err "-r option not allowed in autobuild mode"
|
|
fi
|
|
|
|
autobuild
|
|
|
|
elif [[ -z $autobuildmode ]] ; then
|
|
getbuildfileanddir
|
|
manualbuild
|
|
fi
|
|
else
|
|
|
|
# Get the name of the default source file
|
|
getbuildfileanddir
|
|
manualbuild
|
|
fi
|
|
# End of script
|