#!/bin/sh fetch_src() { [ "$source" ] || return 0 for i in $source; do i=${i%::noextract} case $i in *::*) sn=${i%::*};; *) sn=${i##*/};; esac case ${i#*::} in http://*|https://*|ftp://*) if [ ! -f $APKG_SOURCE_DIR/$sn ]; then [ -f $APKG_SOURCE_DIR/$sn.tmp ] && resume="-C -" msg "fetching ${i#*::} to $APKG_SOURCE_DIR/$sn.tmp" curl $resume -L -o $APKG_SOURCE_DIR/$sn.tmp ${i#*::} || { msg "Failed downloading source '$sn'." exit 1 } mv $APKG_SOURCE_DIR/$sn.tmp $APKG_SOURCE_DIR/$sn fi;; esac done } extract_src() { rm -rf $PKG $SRC mkdir -p $PKG $SRC [ "$source" ] || return 0 for i in $source; do case $i in */*) S=$APKG_SOURCE_DIR;; *) S=$HERE;; esac case ${i%::noextract} in *::*) sn=${i%%::*};; *) sn=${i##*/}; sn=${sn%::noextract};; esac case $i in *::noextract) i=${i%::noextract} cp $S/$sn $SRC || { msg "Failed copy '$S/$sn' to '$SRC'." exit 1 };; *.tar.bz2|*.tar.gz|*.tar.lz|*.tar.lzma|*.tar.lzo|*.tar.xz|*.tar.Z|*.tb2|*.tbz|*.tbz2|*.tz2|*.taz|*.tgz|*.tlz|*.txz|*.tZ|*.taZ) msg "Unpacking '$S/$sn'..." tar -p -o -C $SRC -xf $S/$sn || { msg "Failed unpacking source '${i##*/}'." exit 1 };; *) i=${i%::noextract} cp $S/$sn $SRC || { msg "Failed copy '$S/$sn' to '$SRC'." exit 1 };; esac done } detect_buildtype() { if [ -f meson.build ]; then build_type=meson_build elif [ -f configure ]; then build_type=configure_build elif [ -f CMakeLists.txt ]; then build_type=cmake_build elif [ -f setup.py ]; then build_type=python3_build elif [ -f Makefile.PL ]; then build_type=perlmodule_build elif [ -f Makefile ] || [ -f makefile ]; then build_type=makefile_build else msg "failed to detect buildtype" exit 1 fi } _makefile_build() { make make \ PREFIX=/usr \ prefix=/usr \ SYSCONFDIR=/etc \ sysconfdir=/etc \ MANDIR=/usr/share/man \ mandir=/usr/share/man \ LIBDIR=/usr/lib \ PKGCONFIGDIR=/usr/lib/pkgconfig \ DESTDIR=$PKG install } _perlmodule_build() { perl Makefile.PL make make DESTDIR=$PKG install } _cmake_build() { mkdir -p cmakebuild cd cmakebuild cmake \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_SYSCONFDIR=/etc \ -DCMAKE_INSTALL_LIBDIR=lib \ -DCMAKE_INSTALL_LIBEXECDIR=lib \ -DCMAKE_BUILD_TYPE=Release \ -DFETCHCONTENT_FULLY_DISCONNECTED=ON \ -DCMAKE_C_FLAGS_RELEASE="$CFLAGS" \ -DCMAKE_CXX_FLAGS_RELEASE="$CXXFLAGS" \ $build_opt $@ \ -G Ninja .. if [ -f build.ninja ]; then ninja DESTDIR=$PKG ninja install else cmake --build build DESTDIR=$PKG cmake --install build fi } _python3_build() { python3 setup.py build python3 setup.py install --prefix=/usr --root=$PKG --optimize=1 } _configure_build() { ./configure \ --prefix=/usr \ --sysconfdir=/etc \ --localstatedir=/var \ --libdir=/usr/lib \ --libexecdir=/usr/lib \ --infodir=/usr/share/info \ --mandir=/usr/share/man \ $build_opt $@ make make DESTDIR=$PKG install } _meson_build() { meson setup _meson_build \ --prefix=/usr \ --libdir=/usr/lib \ --libexecdir=/usr/lib \ --includedir=/usr/include \ --datadir=/usr/share \ --mandir=/usr/share/man \ --infodir=/usr/share/info \ --localedir=/usr/share/locale \ --sysconfdir=/etc \ --localstatedir=/var \ --sharedstatedir=/var/lib \ --buildtype=plain \ --auto-features=auto \ --wrap-mode=nodownload \ -Db_lto=true \ -Db_pie=true \ -Db_thinlto_cache=true \ $build_opt || return $? meson compile -C _meson_build || return $? DESTDIR=$PKG meson install --no-rebuild -C _meson_build || return $? } apply_patch() { [ "$skip_patch" ] && { unset skip_patch # allowing manual patch in build() using apply_patch return 0 } if [ "$source" ]; then for p in $source; do case ${p%::noextract} in *::*) pn=${p%::*};; *) pn=${p##*/};; esac case $pn in *.patch|*.diff) msg "applying path '$SRC/$pn'." patch ${patch_opt:--p1} -t -i $SRC/$pn || exit 1;; esac done fi } msg() { echo "[${name:-...}] $@" } exitonerror() { [ "$?" = 0 ] || { echo "error status $?"; exit 1; } } buildstatus() { [ "$?" = 0 ] || { msg "build package '$name-$version-$release' failed on '$1'." exit 1 } } build_src() { cd $SRC if [ "$build_dir" ]; then if [ ! -d "$build_dir" ]; then msg "Source directory '$build_dir' not found." exit 1 fi elif [ -d $name-$version ]; then build_dir=$name-$version elif [ -d "$(ls -1 --group-directories-first | head -n1)" ]; then build_dir=$(ls -1 --group-directories-first | head -n1) fi # cd into extracted source directory if [ "$build_dir" ]; then cd $build_dir fi apply_patch export DESTDIR=$PKG export DEST_DIR=$PKG # p7zip export INSTALLROOT=$PKG # syslinux export install_root=$PKG # glibc export INSTALL_ROOT=$PKG # qt5 if [ ! "$source" ]; then # dummy pkg mkdir -p $PKG/usr elif [ "$(command -v pkg_build)" ]; then (set -e -x; pkg_build) buildstatus pkg_build else if [ "$(command -v pre_build)" ]; then (set -e -x; pre_build) buildstatus pre_build fi if [ ! "${build_type}" ]; then detect_buildtype fi (set -e -x; _${build_type}) buildstatus _${build_type} if [ "$(command -v post_build)" ]; then (set -e -x; post_build) buildstatus post_build fi fi msg "build $name-$version-$release success" # compress man pages if [ -d $PKG/usr/share/man ]; then # only keep man[1-8] man pages for m in $PKG/usr/share/man/*; do case ${m##*/} in man[1-8]) continue;; esac rm -rf $m done find $PKG/usr/share/man -type f -exec gzip -9 {} \; for i in $( find $PKG/usr/share/man -type l ) ; do ln -s $( readlink $i ).gz $i.gz ; rm $i ; done fi # compress info pages if [ -d $PKG/usr/share/info ]; then rm -f $PKG/usr/share/info/dir find $PKG/usr/share/info -type f -exec gzip -9 {} \; fi # possible conflicts find $PKG -name "fonts.dir" -exec rm {} \; find $PKG -name "fonts.scale" -exec rm {} \; find $PKG -name "perllocal.pod" -exec rm {} \; find $PKG -name "charset.alias" -exec rm {} \; if [ ! "$keep_static" ]; then find $PKG -name "*.a" -exec rm {} \; fi if [ ! "$keep_libtool" ]; then find $PKG -name "*.la" -exec rm {} \; fi if [ ! "$keep_locale" ]; then rm -rf $PKG/usr/share/locale rm -rf $PKG/usr/lib/locale fi if [ ! "$keep_doc" ]; then rm -rf $PKG/usr/share/doc rm -rf $PKG/usr/doc fi # strip binaries and libraries if [ ! "$no_strip" ]; then find $PKG | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-all 2>/dev/null find $PKG | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-unneeded 2>/dev/null find $PKG | xargs file | grep "current ar archive" | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-debug 2>/dev/null fi # runit service for s in $sv; do [ -f $SRC/$s ] || exit 1 case $s in run) install -Dm755 $SRC/$s $PKG/etc/sv/$name/run ln -s ../../../run/runit/supervise.$name $PKG/etc/sv/$name/supervise;; finish) install -Dm755 $SRC/$s $PKG/etc/sv/$name/finish;; *.run) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/run ln -s ../../../run/runit/supervise.${s%.*} $PKG/etc/sv/${s%.*}/supervise;; *.finish) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/finish;; *.*) install -Dm644 $SRC/$s $PKG/etc/sv/${s%%.*}/${s#*.};; *) install -Dm644 $SRC/$s $PKG/etc/sv/$name/$s;; esac done if [ ! "$(ls -1 $PKG)" ]; then msg "\$PKG is empty" exit 1 fi cd $APKG_PACKAGE_DIR spm -b $PKG && { mv package.spm $packagefile } || { msg "Failed packaging $packagefile" exit 1 } if [ ! -f "$HERE"/.files ]; then pkg_updatefiles fi cd $HERE rm -rf $SRC $PKG } pkg_updatefiles() { if [ ! -f "$packagefile" ]; then msg "Package '$packagefile' not found." exit 1 fi msg "'.files' updated." tar -tvf $packagefile | awk '{$3=$4=$5=""; print $0}' | sort -k 3 > "$HERE"/.files } pkg_path() { for r in $APKG_REPO; do [ -f $r/$1/abuild ] && { echo $r/$1 break } done } die() { echo "error: $1" exit 1 } checkdep() { pkg_path $1 >/dev/null || return # track processed pkg to avoid cycle deps process="$process $1" for ii in $(pkg_depends $1); do # if deps already in process list, skip, cycle deps detected echo $process | tr ' ' '\n' | grep -qx $ii && continue # skip if itself in depends list [ "$ii" = "$1" ] && continue # skip if pkg already in deps list echo $DEPS | tr ' ' '\n' | grep -x $ii && continue # check deps checkdep $ii done # will go here if no deps anymore to check, add it to list deps DEPS="$DEPS $1" } pkg_deplist() { for i in $@; do # if already have in list deps, dont check again if [ ! $(echo $DEPS | tr ' ' '\n' | grep -x $i) ]; then checkdep $i fi done echo $DEPS | tr ' ' '\n' } pkg_depends() { _path=$(pkg_path $1) >/dev/null || return 1 grep '# Depends' $_path/abuild | awk -F : '{print $2}' | tr ' ' '\n' | sed '/^$/d' } parsesubopt() { # loop opts until found '-*' shift; while [ "$1" ]; do case $1 in -*) break;; *) echo $1;; esac shift done } pkg_outdate() { spm -a | while read -r n v; do [ "$(pkg_path $n)" ] || continue nv="$(grep ^version= $(pkg_path $n)/abuild | awk -F = '{print $2}')-$(grep ^release= $(pkg_path $n)/abuild | awk -F = '{print $2}')" [ "$nv" ] || continue [ "$v" = "$nv" ] || echo "$n $v -> $nv" done } pkg_sysup() { msg "Checking for outdated packages..." od=$(pkg_outdate | awk '{print $1}') if [ ! "$od" ]; then msg "No outdated packages." exit 0 else totalpkg=$(echo $od | tr ' ' '\n' | wc -l) echo echo $od echo msg "Press ENTER to continue upgrade $totalpkg packages." msg "Press Ctrl + C to abort." read -r null apkg -u $od || die fi } parseopts() { while [ "$1" ]; do case $1 in -I) pkg_depinstalll $(parsesubopt $@); exit 0;; -D) pkg_deplist $(parsesubopt $@); exit 0;; -f) forcerebuild=1;; -i) install=1;; -u) upgrade=1;; -o) downloadonly=1;; -U) pkg_sysup; exit 0;; -d) pkg_depends $2; exit 0;; -p) pkg_path $2; exit 0;; -s) pkg_search $2; exit 0;; -*) msg "invalid option '$1'"; exit 1;; *) pkg="$pkg $1";; esac shift done } updateopts() { while [ "$1" ]; do case $1 in -*) newopt="$newopt $1";; esac shift done echo $newopt } pkg_search() { if [ ! "$APKG_REPO" ]; then msg "No repo configured." exit 1 fi [ "$1" ] && grep=grep || grep=cat find $APKG_REPO -type f -name abuild -printf '%h\n' | rev | awk -F / '{print $1}' | rev | $grep $1 } pkg_depinstalll() { for p in $@; do [ -s $SPM_PKGDB/$p ] && continue pkg="$pkg $p" done set -- $pkg msg "Solving dependencies..." for p in $(pkg_deplist $@); do [ -s $SPM_PKGDB/$p ] && continue installthis="$installthis $p" done if [ ! "$installthis" ]; then msg "Nothing to install. Exiting..." exit 0 fi totalpkg=$(echo $installthis | tr ' ' '\n' | wc -l) echo echo $installthis echo msg "Press ENTER to continue install $totalpkg packages." msg "Press Ctrl + C to abort." read -r null apkg -i $installthis || die } runscript() { if [ -x ./$1-install ]; then msg "Running $1-install script..." if [ "$ROOT" ]; then cat ./$1-install > "$ROOT"/.runscript chmod +x "$ROOT"/.runscript chroot "$ROOT" /.runscript rm -f "$ROOT"/.runscript else ./$1-install fi fi } main() { parseopts $@ set -- $(updateopts $@) for d in $APKG_PACKAGE_DIR $APKG_SOURCE_DIR $APKG_WORK_DIR; do [ -d $d ] || { msg "Directory '$d' not exist"; exit 1; } [ -w $d ] || { msg "Directory '$d' dont have write access"; exit 1; } done if [ "$pkg" ]; then for p in $pkg; do pkg_path $p >/dev/null || continue #echo "[$p] package build starts..." (cd $(pkg_path $p) && apkg $@) [ "$?" = 0 ] || exit 1 done exit 0 fi if [ "$install" ] && [ -s $SPM_PKGDB/${PWD##*/} ]; then msg "Package '${PWD##*/}' already installed." exit 0 fi if [ "$upgrade" ] && [ ! -s $SPM_PKGDB/${PWD##*/} ]; then msg "Package '${PWD##*/}' not installed." exit 0 fi if [ ! -f ./abuild ]; then msg "'abuild' not found." exit 1 fi . ./abuild [ "$name" ] || die "name is empty" [ "$version" ] || die "version is empty" [ "$release" ] || die "release is empty" packagefile=$APKG_PACKAGE_DIR/$name#$version-$release.spm if [ "$downloadonly" ]; then fetch_src exit 0 fi if [ -f "$packagefile" ] && [ ! "$forcerebuild" ]; then if [ ! "$install" ] && [ ! "$upgrade" ]; then msg "Package '$packagefile' found." fi else fetch_src extract_src build_src fi if [ "$install" ]; then runscript pre SPM_ROOT=${ROOT%/} spm -i "$packagefile" || exit $? runscript post elif [ "$upgrade" ]; then runscript pre SPM_ROOT=${ROOT%/} spm -u "$packagefile" || exit $? runscript post fi } umask 022 export HERE=$PWD APKG_REPO="" APKG_CONF="/etc/apkg.conf" APKG_PACKAGE_DIR="$PWD" APKG_SOURCE_DIR="$PWD" APKG_WORK_DIR="$PWD" SPM_PKGDB="${ROOT%/}/var/lib/spm/db" if [ -f $APKG_CONF ]; then . $APKG_CONF fi export SRC=$APKG_WORK_DIR/apkg-src-${PWD##*/} export PKG=$APKG_WORK_DIR/apkg-pkg-${PWD##*/} main $@ exit 0