diff options
Diffstat (limited to 'src')
56 files changed, 5019 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..63f7782 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,2 @@ +pkgconfdir=$(sysconfdir) +include ../common.mk diff --git a/src/abslibre-tools/Makefile b/src/abslibre-tools/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/abslibre-tools/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/abslibre-tools/createworkdir b/src/abslibre-tools/createworkdir new file mode 100755 index 0000000..99214ab --- /dev/null +++ b/src/abslibre-tools/createworkdir @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# CreateWorkDir +# Creates a dir structure for working with Parabola packages + +# Copyright 2010 Nicolás Reynolds + +# ---------- GNU General Public License 3 ---------- + +# This file is part of Parabola. + +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. $(librelib conf.sh) +load_files libretools +check_vars libretools WORKDIR REPOS ABSLIBREGIT || exit 1 + +[[ ! -d ${WORKDIR} ]] && { # Create the WORKDIR + + msg "Creating WORKDIR on ${WORKDIR}" + mkdir -p ${WORKDIR} ||{ + error "Could not create ${WORKDIR}"; exit 1 + } + +} + +for _repo in "${REPOS[@]}"; do # Create the staging dirs + + [[ ! -d ${WORKDIR}/staging/${_repo} ]] && { + mkdir -p ${WORKDIR}/staging/${_repo} || { + error "Can't create ${WORKDIR}/staging/${_repo}" + exit 1 + } + } + +done + +[[ ! -d ${WORKDIR}/abslibre/.git ]] && { + msg "Cloning into ABSLibre" + CMD="git clone ${ABSLIBREGIT} ${WORKDIR}/abslibre" + ${CMD} || { + error "Could not clone ABSLibre" + plain "Try running this command:" + echo + plain "${CMD}" + exit 1 + } +} + +msg "Finished, your packaging dir tree looks like this now:" +ls --color=always ${WORKDIR}/*/* + +exit 0 diff --git a/src/abslibre-tools/diff-unfree b/src/abslibre-tools/diff-unfree new file mode 100755 index 0000000..07f2ca2 --- /dev/null +++ b/src/abslibre-tools/diff-unfree @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# This script will help you diff a *-libre PKGBUILD against the unfree one +# to check for updates. +# Copyright 2010 Nicolás Reynolds + +# ---------- GNU General Public License 3 ---------- + +# This file is part of Parabola. + +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. $(librelib conf.sh) +load_files libretools +check_vars libretools DIFFTOOL || exit 1 + +cmd=${0##*/} + +usage() { + echo "Usage: $cmd [community|packages] [unfree-package] [repo]" + echo "Usage: $cmd --help" + echo "Helps you diff build scripts from ABSLibre against (Unfree) ABS." + echo "" + echo "Package name and repo will we guessed if you don't specify them." +} + +main() { + if [[ "$1" == "--help" ]]; then + usage + exit 0 + fi + + local package_guess=${PWD##*/} + local repo=${1:-$(basename ${PWD%/*})} + local package=${2:-${package_guess%-libre}} + local trunk=${3:-trunk} + + svnrepo="packages" + case $repo in + community*) svnrepo="community";; + multilib*) svnrepo="community";; + *) :;; + esac + + if [[ ! -r PKGBUILD ]]; then + error "This is not a build dir." + exit 1 + fi + + + tmp_dir="$(mktemp --tmpdir -d ${package}.XXXXXX)" + if [[ ! -d "${tmp_dir}" ]]; then + error "Can't create temp dir" + exit 1 + fi + unfree_dir="${tmp_dir}/${svnrepo}/${package}/${trunk}" + + pushd "${tmp_dir}" &>/dev/null + + msg "Getting diff from $repo/$package..." + + svn checkout --depth=empty svn://svn.archlinux.org/$svnrepo &>/dev/null + + cd ${svnrepo} + svn update ${package} + + # Back to start dir + popd &>/dev/null + + msg "Diffing files" + + for _file in ${unfree_dir}/*; do + msg2 "$(basename "${_file}")" + ${DIFFTOOL} "$PWD/$(basename "${_file}")" "${_file}" + done +} + +main "$@" diff --git a/src/abslibre-tools/libreaddiff b/src/abslibre-tools/libreaddiff new file mode 100755 index 0000000..03d0ad0 --- /dev/null +++ b/src/abslibre-tools/libreaddiff @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -e +# -*- coding: utf-8 -*- +# Copyright (C) 2011, 2012 Michał Masłowski <mtjm@mtjm.eu> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $(librelib conf.sh) +load_files libretools +check_vars libretools WORKDIR + +for arg in "$@" ; do + case "$arg" in + -h|--h|--he|--hel|--help|-\?) + echo 'Usage: libreaddiff repo [arch] + +This script outputs a diff of package names and versions in repo +between pacman'\''s sync db and abslibre checkout.' >&2 + exit 0 + ;; + esac +done + +# The repo to find missing packages in. +repo=$1 +# The arch to check in Arch repos, other will have all arches checked. +arch=${2:-mips64el} +# A Python tuple of repos which don't have arch=any packages. +archrepos='("core", "extra", "community")' + +diff -U0 \ + <( ( + cd /var/lib/pacman/sync + for f in $repo.db ; do + tar xOf $f | python -c 'import sys +arch = None +name = None +version = None +it = iter(sys.stdin) +try: + while True: + line = next(it) + if line == "%ARCH%\n": + arch = next(it) + if arch == "'"$arch"'\n" or "'$repo'" not in '"$archrepos"': + print("%s-%s" % (name.strip(), version.strip())) + if line == "%NAME%\n": + name = next(it) + if line == "%VERSION%\n": + version = next(it) +except StopIteration: + pass +' + done + ) | sort ) \ + <( ( + cd "${WORKDIR}/abslibre" + # Needed to not include pkgnames specific to other arches. + CARCH=$arch + for f in $repo/* ; do + load_PKGBUILD "$f/PKGBUILD" || continue + is_here=false + for arc in ${arch[@]} ; do + if [ "$arc" = "any" -o "$arc" = "$CARCH" ] ; then + is_here=true + break + fi + done + if [ "$is_here" = "true" ] ; then + for name in ${pkgname[@]} ; do + if [ -z "$epoch" ] ; then + echo $name-$pkgver-$pkgrel + else + echo $name-$epoch:$pkgver-$pkgrel + fi + done + fi + done + ) | sort ) | sed -rn 's/^[+-][^+-].+$/&/p' diff --git a/src/abslibre-tools/librerelease b/src/abslibre-tools/librerelease new file mode 100755 index 0000000..5913670 --- /dev/null +++ b/src/abslibre-tools/librerelease @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +# Librerelease +# Uploads packages into [staging] + +# Copyright 2010 Nicolás Reynolds +# Copyright 2013 Luke Shumaker +# For just the create_signature() function: +# Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. $(librelib conf.sh) + +function usage { + print "Usage: %s [OPTIONS]" "${0##*/}" + echo + print 'This script uploads packages on $WORKDIR/stagging' + print "to parabola server." + echo + print "Options:" + print ' -c Clean packages on $WORKDIR/staging' + print " -l Only list packages but not upload them" + print " -n Dry-run; don't actually do anything" + print " -h Show this message" +} + +function list_packages { + find "$WORKDIR/staging/" -mindepth 1 -type d -not -empty -printf '%f\n' | sort | + while read -r repo; do + msg2 "$repo" + find "${WORKDIR}/staging/${repo}" -type f -printf "%f\n" | sort + done +} + +# This function is taken almost verbatim from makepkg +create_signature() { + local ret=0 + local filename="$1" + msg "$(gettext "Signing package...")" + + local SIGNWITHKEY="" + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY="-u ${GPGKEY}" + fi + # The signature will be generated directly in ascii-friendly format + gpg --detach-sign --use-agent ${SIGNWITHKEY} "$filename" &>/dev/null || ret=$? + + + if (( ! ret )); then + msg2 "$(gettext "Created signature file %s.")" "$filename.sig" + else + error "$(gettext "Failed to sign package file.")" + return $ret + fi +} + +function sign_packages { + if [ -z "${GPG_AGENT_INFO}" ]; then + warning "It's better to use gpg-agent to sign packages in batches" + fi + + for package in $(find "${WORKDIR}/staging/" -type f -iname '*.pkg.tar.?z'); do + if [ -f "${package}.sig" ]; then + msg2 "Package signature found, verifying..." + + # Verify that the signature is correct, else remove for re-signing + if ! gpg --quiet --verify "${package}.sig" >/dev/null 2>&1; then + error "Failed! Re-signing..." + rm -f "${package}.sig" + fi + fi + + if ! [ -f "${package}.sig" ]; then + create_signature "$package" || return 2 + fi + done +} + +# Remove everything that's not a package or a signature +function clean_non_packages { + find $WORKDIR/staging/ -type f \ + \! -iname "*.pkg.tar.?z" -a \! -iname "*.pkg.tar.?z.sig" \ + -delete +} + +# Clean everything if not on dry-run mode +function clean { + if [[ -n "${dryrun}" ]]; then + : + else + msg "Removing files from local staging directory" + # use '-exec rm' instead of '-delete' to be verbose + find "${WORKDIR}/staging" -type f -exec rm -fv {} + + fi +} + +function main { + if [ -w / ]; then + error "This program should be run as regular user" + return 1 + fi + + # Parse options + local dryrun="" + local mode="release_packages" + while getopts 'clnh' arg; do + case $arg in + c) mode=clean ;; + l) mode=list_packages ;; + n) dryrun="--dry-run" ;; + h) mode=usage ;; + *) usage >/dev/stderr; return 1 ;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# != 0 ]]; then + usage >/dev/stderr + return 1 + fi + + if [[ $mode == usage ]]; then + usage + return 0 + fi + + load_files makepkg + check_vars makepkg GPGKEY + load_files libretools + check_vars libretools WORKDIR REPODEST || return 1 + # The following variables are actually optional + #check_vars libretools HOOKPRERELEASE HOOKPOSTRELEASE || return 1 + + lock 10 "${WORKDIR}/staging.lock" 'Waiting for an exclusive lock on the staging directory' + "$mode" +} + +function release_packages { + if [[ -n $HOOKPRERELEASE ]]; then + msg "Running HOOKPRERELEASE..." + bash -c "${HOOKPRERELEASE}" + fi + + clean_non_packages + sign_packages || return 1 + + # Make the permissions of the packages 644 otherwise the user will get access + # denied error when they try to download (rsync --no-perms doesn't seem to + # work). + find ${WORKDIR}/staging -type f -exec chmod 644 {} \; + find ${WORKDIR}/staging -type d -exec chmod 755 {} \; + + msg "%s to upload" $(du -h -d 0 ${WORKDIR}/staging | tr "\t" " " | cut -d" " -f1) + msg "Uploading packages..." + if ! rsync --recursive \ + ${dryrun} \ + --no-group \ + --no-perms \ + --copy-links \ + --hard-links \ + --partial \ + --prune-empty-dirs \ + --human-readable \ + --progress \ + -e "ssh " \ + ${WORKDIR}/staging \ + ${REPODEST}/ + then + error "Sync failed, try again" + return 1 + fi + + clean + + msg "Running db-update on repos" + ssh ${REPODEST%%:*} dbscripts/db-update + + if [[ -n $HOOKPOSTRELEASE ]]; then + msg "Running HOOKPOSTRELEASE..." + bash -c "${HOOKPOSTRELEASE}" + fi + + return 0 +} + +main "$@" diff --git a/src/abslibre-tools/librestage b/src/abslibre-tools/librestage new file mode 100755 index 0000000..57846fc --- /dev/null +++ b/src/abslibre-tools/librestage @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# LibreStage +# Prepares packages for upload + +# Copyright 2010-2011 Nicolás Reynolds +# Copyright 2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. $(librelib conf.sh) + +cmd=${0##*/} +usage() { + print "Usage: %s REPO [REPO2 REPO3...]" "$cmd" + print "Stages the package(s) build by ./PKGBUILD for upload." + echo + print "The package(s) are staged for the named repositories." + print "It is in general a bad idea to stage a package on multiple" + print "repositories, but it supported by this tool." +} + +main() { + if [[ -w / ]]; then + error "This program should be run as regular user" + return 1 + fi + + # Parse options, set up + while getopts 'h' arg; do + case $arg in + h) usage; return 0;; + *) usage >/dev/stderr; return 1;; + esac + done + repos=("$@") + if [[ ${#repos[@]} -eq 0 ]]; then + usage >>/dev/stderr + return 1; + fi + + [[ ! -e ./PKGBUILD ]] && { + error "PKGBUILD not found" + return 1 + } + + # Load configuration + load_files libretools + check_vars libretools WORKDIR ARCHES || return 1 + load_files makepkg # for PKGDEST, which is optional + + # Load the PKGBUILD + load_PKGBUILD + + # Now for the main routine. + staged=false + slock 10 "${WORKDIR}/staging.lock" 'Waiting for a shared lock on the staging directory' + for CARCH in "${ARCHES[@]}" any; do + for _pkgname in "${pkgname[@]}"; do + pkgfile=${_pkgname}-$(get_full_version $_pkgname)-${CARCH}${PKGEXT} + pkgpath="$(find . "${PKGDEST:-.}" -maxdepth 1 -type f -name "$pkgfile"|sed 1q)" + + if [[ ! -f "${pkgpath}" ]]; then + continue + else + pkgpath="$(readlink -f "$pkgpath")" + fi + + msg "Found ${pkgfile}" + + canonical="" # is empty for the first iteration, set after that + for repo in "${repos[@]}"; do + mkdir -p "${WORKDIR}/staging/${repo}" + if [[ -z $canonical ]]; then + canonical="${WORKDIR}/staging/${repo}/${pkgfile}" + cmd=(cp "$pkgpath" "$canonical") + else + cmd=(ln "$canonical" "${WORKDIR}/staging/${repo}/${pkgfile}") + fi + if "${cmd[@]}"; then + msg2 "%s staged on [%s]" "$_pkgname" "$repo" + staged=true + else + error "Can't put %s on [%s]" "$_pkgname" "$repo" + return 1 + fi + done + done + done + + if $staged ; then + return 0 + else + error "No package was staged" + return 1 + fi +} + +main "$@" @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# Copyright 2010 Joshua Ismael +# Copyright 2010 Nicolás Reynolds +# Copyright 2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +cmd=${0##*/} +usage() { + echo "Usage: $cmd [-h] pkgname-from-aur1 [pkgname-from-aur2 ...]" + echo + echo "This script will download packages from AUR to the current" + echo "directory and check their license for nonfree issues. This does" + echo "not mean that they are free; they may be incorrectly labeled, or" + echo "have other freedom issues. It's a tool to help Parabola" + echo "packagers, not to help users install things directly from AUR." +} + +main() { + while getopts 'h' arg; do + case $arg in + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + if [[ $# -lt 1 ]]; then + usage >&2 + return 1 + fi + + . $(librelib conf.sh) + load_files libretools + check_vars libretools DIFFTOOL || exit 1 + + local startdir="$(pwd)" + local missing_deps=() + local ret=0 + local pkg + local copy_new + local copy_old + for pkg in "$@"; do + pkg="${pkg%%[<>=]*}" # remove the version + msg "Processing package: %s" "$pkg" + copy_new="$startdir/$pkg" + copy_old= + + if [[ -f "${copy_new}/PKGBUILD" ]]; then + warning "%s already exists, will compare with new version." "$pkg" + + # Store our copy of the PKGBUILD dir + copy_old=$copy_new + copy_new="$(mktemp --tmpdir -d aur-${pkg}.new.XXXX)/$pkg" + cd "${copy_new%/*}" + fi + + msg2 "Downloading" + local url="https://aur.archlinux.org/packages/${pkg:0:2}/$pkg/$pkg.tar.gz" + set -o pipefail + if ! wget -O- -q "$url" | bsdtar xf -; then + ret=$(($ret|2)) + error "Couldn't get %s" "$pkg" + continue + fi + set +o pipefail + + if [[ -n $copy_old ]]; then + msg2 "Diffing files" + cd "$copy_new" + + # Diff all files with our difftool + local diffed=false + for file in *; do + if ! cmp -s "${copy_old}/${file}" "${copy_new}/${file}" ; then + warning "%s != %s" "${copy_old}/${file}" "${copy_new}/${file}" + diffed=true + "${DIFFTOOL}" "${copy_old}/${file}" "${copy_new}/${file}" + fi + done + if $diffed; then + read -p "Press enter to continue." + fi + + # Go back to our copy to continue working + cd "$copy_old" + rm -rf -- "${copy_new%/*}" + else + cd "$copy_new" + fi + + load_PKGBUILD + + ################################################################ + + pkgbuild-check-nonfree -c + case $? in + 0) :;; + 15) warning "This PKGBUILD links to known unfree packages";; + *) warning "pkgbuild-check-nonfree failed to run";; + esac + + ################################################################ + + local s=0 + pkgbuild-check-licenses || s=$? + for i in 1 2 4; do + if [[ $i -eq $(($s & $i)) ]]; then + case $i in + 1) warning "pkgbuild-check-licenses encountered an error";; + 2) warning "This PKGBUILD has an uncommon license";; + 4) warning "This PKGBUILD has a known nonfree license";; + esac + fi + done + unset s + + ################################################################ + + local _deps=( + # depends + "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" + # mksource depends + "${mkdepends[@]}" + ) + local _dep + msg2 "Checking dependencies" + for _dep in "${_deps[@]}"; do + _dep=${_dep/[<>=]*/} + if ! is_built $_dep; then + if ! pacman -Sddp "$_dep" &>/dev/null ; then + plain "%s: will be downloaded from AUR" "$_dep" + missing_deps+=($_dep) + fi + else + plain "%s: is on repos" "$_dep" + fi + done + cd "$startdir" + done + + if [[ ${#missing_deps[*]} -gt 0 ]]; then + msg "Retrieving missing deps: %s" "${missing_deps[*]}" + "$0" "${missing_deps[@]}" + ret=$(($ret|$?)) + fi + return $ret; +} + +main "$@" diff --git a/src/chroot-tools/.gitignore b/src/chroot-tools/.gitignore new file mode 100644 index 0000000..80e1000 --- /dev/null +++ b/src/chroot-tools/.gitignore @@ -0,0 +1,5 @@ +makechrootpkg.sh* +!makechrootpkg.sh.patch + +arch-nspawn* +mkarchroot* diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile new file mode 100644 index 0000000..0540636 --- /dev/null +++ b/src/chroot-tools/Makefile @@ -0,0 +1,44 @@ +# These files are coming from devtools +copy_files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in +# These are programs that we will use internally, but shouldn't be in PATH +libexecs = mkarchroot arch-nspawn distcc-tool chcleanup +no-progs = $(libexecs) +# These are the shell libraries we will use +libs = makechrootpkg.sh $(wildcard hooks-*.sh) + +pkglibexecdir = $(libexecdir)/libretools/chroot +clean_files = makechrootpkg.sh.ugly* *~ +include ../../common.mk + +# Usage: $(call indent,FILENAME) +# Command to auto-indent a file. +indent = emacs --batch $1 \ + --eval '(setq sh-basic-offset 8)' \ + --eval '(indent-region (point-min) (point-max) nil)' \ + -f save-buffer &>/dev/null + +# makechrootpkg.sh is special, we patch it and do fancy stuff +# The flow is: +# $(devtoolsdir)/*.in -> *.sh.in + *.sh.patch -> *.sh.ugly -> *.sh + +makechrootpkg.sh.in: %.sh.in: $(devtoolsdir)/%.in + cp $< $@ +makechrootpkg.sh.ugly: %.ugly: %.in %.patch Makefile + cp $*.in $@ + @echo 'PATCH $@ $*.patch'; patch $@ $*.patch || { rm -f -- '$@'; false; } +makechrootpkg.sh: %: %.ugly Makefile + @echo 'EDIT < $< > $@'; $(edit) <'$<' >'$@' || { rm -f -- '$@'; false; } + @echo 'INDENT $@'; $(call indent,$@) || { rm -f -- '$@'; false; } + +mkarchroot: mkarchroot.in Makefile + @echo '< $< M4_EDIT | SED > $@' + @<'$<' $(edit) | sed 's|arch-nspawn|$$(librelib chroot/&)|' >'$@' || { rm -f -- '$@'; false; } + @echo 'CHMOD $<'; chmod 755 "$@" || { rm -f -- '$@'; false; } + +archroot: %: %.in Makefile + @echo "GEN $@" + @$(edit) <"$<" >"$@" || { rm -f -- '$@'; false; } + @chmod 755 "$@" || { rm -f -- '$@'; false; } + +distcc-tool.pot: distcc-tool + xgettext --omit-header -i --from-code=UTF-8 -L shell --keyword={error,errusage} -o $@ $< diff --git a/src/chroot-tools/chcleanup b/src/chroot-tools/chcleanup new file mode 100755 index 0000000..a43065b --- /dev/null +++ b/src/chroot-tools/chcleanup @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -eE +# (c) Nicolás Reynolds <fauno@parabola.nu> +# Released under GPLv3 +# +# Performs chroot cleanup smartly, it only removes the unneeded packages or +# leaves you with a cleansystem +# +# See: HOOKPREBUILD + +DRYRUN=${DRYRUN:-false} + +################################################################################ +# Define these here to avoid having dependencies on outside files + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +plain() { + local mesg="$(_ "$1")"; shift + printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 +} + +msg() { + local mesg="$(_ "$1")"; shift + printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 +} + +msg2() { + local mesg="$(_ "$1")"; shift + printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 +} + +warning() { + local mesg="$(_ "$1")"; shift + printf "${YELLOW}==> $(gettext "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 +} + +error() { + local mesg="$(_ "$1")"; shift + printf "${RED}==> $(gettext "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 +} + +################################################################################ + +if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then + error "(chcleanup): Must be run inside of a chroot" + exit 1 +fi + +source /etc/libretools.d/chroot.conf +# If we're running makepkg +if [ -f PKGBUILD ]; then + export CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")" + source PKGBUILD + CHROOTEXTRAPKG+=("${depends[@]}" + "${makedepends[@]}" + "${checkdepends[@]}") +fi + +msg "Cleaning chroot..." +msg2 "Fetching updated package lists..." +pacman -Sy || { + warning "There was an error updating package lists, ignoring." +} + +# Setup the temporary directory +TEMPDIR="$(mktemp --tmpdir -d $(basename $0).XXXXX)" +trap "rm -rf '$TEMPDIR'" EXIT + +cp -a /var/lib/pacman/sync "${TEMPDIR}/" +pkglist="${TEMPDIR}"/pkglist.txt + +# Get the full list of packages needed by dependencies, including the base system +msg2 "Creating a full list of packages..." +pacman -b "${TEMPDIR}" \ + -Sp --print-format "%n" base-devel "${CHROOTEXTRAPKG[@]}" >"$pkglist" || { + ret=$? + error "Could not create a full list of packages, exiting." + plain "This is likely caused by a dependency that could not be found." + exit $ret +} + +# Diff installed packages against a clean chroot then remove leftovers +packages=($(comm -23 <(pacman -Qq | sort -u) \ + <(sort -u "${pkglist}"))) + +if [[ ${#packages[@]} = 0 ]]; then + msg2 "No packages to remove" +else + msg2 "Removing %d packages" ${#packages[@]} + + if ${DRYRUN}; then + echo "${packages[*]}" + else + # Only remove leftovers, -Rcs removes too much + pacman --noconfirm -Rn "${packages[@]}" + fi +fi diff --git a/src/chroot-tools/chroot.conf b/src/chroot-tools/chroot.conf new file mode 100644 index 0000000..3b3c445 --- /dev/null +++ b/src/chroot-tools/chroot.conf @@ -0,0 +1,13 @@ +# The full path to the chroot is +# $CHROOTDIR/$CHROOT/$COPY +# where $COPY is set at runtime. +# See `librechroot help` for details. +CHROOTDIR=/var/lib/archbuild +CHROOT=default + +# Extra packages to have installed on the chroot. +# This is in addition to CHROOTPKG=(base-devel) +CHROOTEXTRAPKG=() +#CHROOTEXTRAPKG+=(distcc-nozeroconf socat) # for BUILDENV+=(distcc) +#CHROOTEXTRAPKG+=(ccache) # for BUILDENV+=(ccache) +#CHROOTEXTRAPKG+=(libretools) # for running libremakepkg inside the chroot diff --git a/src/chroot-tools/distcc-tool b/src/chroot-tools/distcc-tool new file mode 100755 index 0000000..7633029 --- /dev/null +++ b/src/chroot-tools/distcc-tool @@ -0,0 +1,244 @@ +#!/usr/bin/env bash +# -*- tab-width: 4; sh-basic-offset: 4 -*- +# distcc-tool + +# Copyright 2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +# This program has very few dependencies: +# - bash: I don't know what version, I use fairly modern features +# - socat +# - cat: any version +# - rm: any version +# - sed: any version +# On Parabola, this means the packages: +# bash, coreutils, sed, socat + +if ! type gettext &>/dev/null; then + gettext() { echo "$@"; } +fi + +panic() { + echo "$(gettext 'panic: malformed call to internal function')" >&2 + exit 1 +} + +error() { + mesg="$(gettext "$1")"; shift + printf "$(gettext 'ERROR:') $mesg\n" "$@" >&2 + exit 1 +} + +print() { + local mesg=$1 + shift + printf -- "$(gettext "$mesg")\n" "$@" +} + +usage() { + print "Usage: $0 COMMAND [COMMAND-ARGS]" + print "Tool for using distcc within a networkless chroot" + echo + print "Commands:" + print ' help print this message' + print ' odaemon CHROOTPATH daemon to run outside of the chroot' + print ' idaemon DISTCC_HOSTS daemon to run inside of the chroot' + print ' rewrite DISTCC_HOSTS prints a rewritten version of DISTCC_HOSTS' + print ' client HOST PORT connects stdio to TCP:$HOST:$PORT' + print 'Commands: for internal use' + print ' server counterpart to client; spawned by odaemon' +} + +errusage() { + if [[ $# -gt 0 ]]; then + fmt="$(gettext "$1")"; shift + printf "$(gettext 'ERROR:') $fmt\n" "$@" >&2 + fi + usage >&2 + exit 1 +} + +main() { + local cmd=$1 + shift + case "$cmd" in + help) + [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd" + usage;; + odaemon|idaemon|rewrite) + [[ $# -eq 1 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + client) + [[ $# -eq 2 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + server) + [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + *) errusage 'unknown subcommand: %s' "$cmd";; + esac +} + +################################################################################ +# DISTCC_HOSTS parser # +################################################################################ + +# usage: parse_DISTCC_HOSTS true|false DISTCC_HOSTS +# parses DISTCC_HOSTS and: +# $1==true : It sets up port forwarding for inside the choot, sleep forever +# $1==false: Prints a modified version of DISTCC_HOSTS that uses the forwarded +# ports that were set up when $1==true. +parse_DISTCC_HOSTS() { + { [[ $# -eq 2 ]] && { [[ $1 == true ]] || [[ $1 == false ]]; }; } || panic + local forward_ports=$1 + local DISTCC_HOSTS=$2 + + local pids=() # child pids + local newhosts=() + local newport=8000 # next port to be used for port forwarding + + # This is based on the grammar specified in distcc(1) + local HOSTSPEC + for HOSTSPEC in $(sed 's/#.*//' <<<"$DISTCC_HOSTS"); do + case "$HOSTSPEC" in + # LOCAL_HOST + localhost|localhost/*|--localslots=*|--localslots_cpp=*) + # "localhost" runs commands directly, not talking to distccd at + # localhost, use an IP or real hostname for that. + # So, just pass these through. + newhosts+=("$HOSTSPEC") + ;; + # SSH_HOST + *@*) + # SSH_HOST doesn't allow custom port numbers, and even if it + # did, ssh would complain about MITM. Instead, we'll count on + # ssh ProxyCommand being configured to used `client`. + newhosts+=("$HOSTSPEC") + ;; + # GLOBAL_OPTION + --*) + # pass these through + newhosts+=("$HOSTSPEC") + ;; + # ZEROCONF + +zeroconf) + error "%s does not support the +zeroconf option" "$0" + exit 1 + ;; + # TCP_HOST or OLDSTYLE_TCP_HOST + *) + declare HOSTID= PORT= LIMIT= OPTIONS= + if [[ $HOSTSPEC =~ ^([^:/]+)(:([0-9]+))?(/([0-9]+))?(,.*)?$ ]]; then + # TCP_HOST + HOSTID=${BASH_REMATCH[1]} + PORT=${BASH_REMATCH[3]} + LIMIT=${BASH_REMATCH[5]} + OPTIONS=${BASH_REMATCH[6]} + elif [[ $HOSTSPEC =~ ^([^:/]+)(/([0-9]+))?(:([0-9]+))?(,.*)?$ ]]; then + # OLDSTYLE_TCP_HOST + HOSTID=${BASH_REMATCH[1]} + LIMIT=${BASH_REMATCH[3]} + PORT=${BASH_REMATCH[5]} + OPTIONS=${BASH_REMATCH[6]} + else + error "Could not parse HOSTSPEC: %s" "$HOSTSPEC" + fi + + # set up port forwaring + if $forward_ports; then + socat TCP-LISTEN:${newport},fork SYSTEM:"$0 client $HOSTID ${PORT:-3632}" & + pids+=($!) + fi + + # add the forwarded port + local newhost="127.0.0.1:$newport" + [[ -z $LIMIT ]] || newhost+="/$LIMIT" + [[ -z $OPTIONS ]] || newhost+="$OPTIONS" + newhosts+=("$newhost") + : $((newport++)) + ;; + esac + done + if $forward_ports; then + if [[ -z "${pids[*]}" ]]; then + # listen on port 8000, but immediatly close, just so that we are + # listening on something + socat TCP-LISTEN:${newport},fork SYSTEM:true & + pids+=($!) + fi + trap "kill -- ${pids[*]}" EXIT + wait "${pids[@]}" + else + printf '%s\n' "${newhosts[*]}" + fi +} + +################################################################################ +# Port forwarding primitives # +################################################################################ + +# Usage: server +# Reads "host port" from the first line of stdin, then connects stdio to the +# specified TCP socket. +server() { + [[ $# -eq 0 ]] || panic + while read -r host port; do + socat STDIO TCP:"$host:$port" + break + done +} + +# Usage: client HOST PORT +# For usage inside of a chroot. It talks through the UNIX-domain socket to an +# instance of `server` outside, in order to connect stdio to the specified TCP +# socket. +client() { + [[ $# -eq 2 ]] || panic + local file=/socket + { printf '%s\n' "$*"; cat; } | socat UNIX-CONNECT:"$file" STDIO +} + +################################################################################ +# High-level routines # +################################################################################ + +# Usage: odaemon CHROOTPATH +# Listens on "$CHROOTPATH/socket" and spawns a `server` for each new connection. +odaemon() { + [[ $# -eq 1 ]] || panic + local chrootpath=$1 + + umask 111 + socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$0 server" & + trap "kill -- $!; rm -f '$chrootpath/socket'" EXIT + wait +} + +# Usage: idaemon DISTCC_HOSTS +# Sets things up inside of the chroot to forward distcc hosts out. +idaemon() { + [[ $# -eq 1 ]] || panic + parse_DISTCC_HOSTS true "$1" +} + +# Usage: rewrite DISTCC_HOSTS +# Prints a modified version of DISTCC_HOSTS for inside the chroot. +rewrite() { + [[ $# -eq 1 ]] || panic + parse_DISTCC_HOSTS false "$1" +} + +main "$@" diff --git a/src/chroot-tools/hooks-chcleanup.sh b/src/chroot-tools/hooks-chcleanup.sh new file mode 100644 index 0000000..09e6dd9 --- /dev/null +++ b/src/chroot-tools/hooks-chcleanup.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euE + +hooks_pre_build+=("clean_chroot") + +clean_chroot() ( + set +x + local copydir=$1 + if $INCHROOT; then + cd /build + sudo -u nobody "$(librelib chroot/chcleanup)" + else + librechroot -l "$copydir" clean-pkgs + fi + r=$?; echo clean_chroot returning $r; return $r +) diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh new file mode 100644 index 0000000..e8120b8 --- /dev/null +++ b/src/chroot-tools/hooks-check.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euE + +hook_check_pkgbuild+=("check_pkgbuild_dependencies") +check_pkgbuild_dependencies() { + local s=0 + sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$? + case $s in + 0) :;; + 15) error "This PKGBUILD links to known unfree packages"; return 1;; + *) warning "pkgbuild-check-nonfree failed to run";; + esac +} + +hook_check_pkgbuild+=("check_pkgbuild_license") +check_pkgbuild_license() { + local s=0 + sudo -EH -u "$LIBREUSER" pkgbuild-check-licenses -f || s=$? + for i in 1 2 4; do + if [[ $i -eq $(($s & $i)) ]]; then + case $i in + 1) warning "pkgbuild-check-licenses encountered an error";; + 2) warning "This PKGBUILD has an uncommon license";; + 4) error "This PKGBUILD has a known nonfree license"; ret=1;; + esac + fi + done +} + +#hook_check_pkgbuild+=("check_pkgbuild_namcap") +check_pkgbuild_namcap() { + sudo -EH -u "$LIBREUSER" namcap PKGBUILD +} + +#hook_check_pkg+=("check_pkg") +check_pkg() { + # TODO + : +} diff --git a/src/chroot-tools/hooks-distcc.sh b/src/chroot-tools/hooks-distcc.sh new file mode 100644 index 0000000..9e42242 --- /dev/null +++ b/src/chroot-tools/hooks-distcc.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euE + +hook_pre_build+=("distcc_start") +hook_post_build+=("distcc_stop") + +_distcc_check() { + local copydir=$1 + local home=$2 + + local files=( + "$copydir/bin/distcc-tool" + "$copydir/run/distcc-tool.pid" + "$home/.makepkg.conf" + "$home/.ssh/config" + ) + + local file_err=false + for files in "${files[@]}"; do + if [[ -f $file ]]; then + file_err=true + error "Auto-generated file already exists, remove it: %s" "$file" + fi + done + if $file_err; then + exit 1 + fi +} + +distcc_start() { + local copydir=$1 + + # Because /{,usr/}{,s}bin are all symlinked together for + # fileystem 2013.05-2 and up, I can take shortcuts when checking for + # existance of programs. + if $NONET && [[ -f "$copydir/bin/socat" && -f "$copydir/bin/distcc" ]]; then + local home + if $INCHROOT; then + home=$LIBREHOME + else + home="$copydir/build" + fi + + _distcc_check + + local _distcc_tool=$(librelib chroot/distcc-tool) + install -m755 "$_distcc_tool" "$copydir/bin/distcc-tool" + + mkdir -p "$home/.ssh" + + printf '%s\n' \ + '/bin/distcc-tool idaemon "$DISTCC_HOSTS" &' \ + 'DISTCC_HOSTS="$(/bin/distcc-tool rewrite "$DISTCC_HOSTS")"' \ + > "$home/.makepkg.conf" + + printf '%s\n' \ + 'Host *' \ + ' ProxyCommand /bin/distcc-tool client %h %p' \ + > "$home/.ssh/config" + + "$_distcc_tool" odaemon "$copydir" & + echo $! > "$copydir/run/distcc-tool.pid" + fi +} + +distcc_stop() { + local copydir=$1 + + local home + if $INCHROOT; then + home=$LIBREHOME + else + home="$copydir/build" + fi + + if [[ -f "$copydir/run/distcc-tool.pid" ]]; then + + odaemon=$(cat "$copydir/distcc-tool.pid") + kill -- "$odaemon" + + rm -f -- \ + "$home/.makepkg.conf" \ + "$home/.ssh/config" \ + "$copydir/bin/distcc-tool" \ + "$copydir/run/distcc-tool.pid" + fi +} diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot new file mode 100755 index 0000000..0b3ad43 --- /dev/null +++ b/src/chroot-tools/librechroot @@ -0,0 +1,325 @@ +#!/usr/bin/env bash +set -euE +# librechroot + +# Copyright 2010 Nicolás Reynolds +# Copyright 2011 Joshua Haase +# Copyright 2012-2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +# HACKING: if a command is added or removed, it must be changed in 3 places: +# - the usage() text +# - the commands=() array +# - the case statement in main() + +. $(librelib conf) +load_files chroot + +. libremessages + +shopt -s nullglob +umask 0022 + +readonly _arch_nspawn=$(librelib chroot/arch-nspawn) +readonly _mkarchroot=$(librelib chroot/mkarchroot) +readonly _makechrootpkg=$(librelib chroot/makechrootpkg.sh) + +# Because the makechrootpkg.sh library functions don't work with -euE +_makechrootpkg() ( + set +euE + . "$_makechrootpkg" + "$@" +) + +# Usage: make_empty_repo $copydir +make_empty_repo() { + local copydir=$1 + mkdir -p "${copydir}/repo" + bsdtar -czf "${copydir}/repo/repo.db.tar.gz" -T /dev/null + ln -s "repo.db.tar.gz" "${copydir}/repo/repo.db" +} + +# Usage: chroot_add_to_local_repo $copydir $pkgfiles... +chroot_add_to_local_repo() { + local copydir=$1; shift + mkdir -p "$copydir/repo" + local pkgfile + for pkgfile in "$@"; do + cp "$pkgfile" "$copydir/repo" + pushd "$copydir/repo" >/dev/null + repo-add repo.db.tar.gz "${pkgfile##*/}" + popd >/dev/null + done +} + +usage() { + eval "$(calculate_directories)" + print "Usage: %s [OPTIONS] COMMAND [ARGS...]" "${0##*/}" + print 'Interacts with an archroot (arch chroot).' + echo + prose 'This is configured with `chroot.conf`, either in + `/etc/libretools.d/`, or `$XDG_CONFIG_HOME/libretools/`. + The variables you may set are $CHROOTDIR, $CHROOT, and + $CHROOTEXTRAPKG.' + echo + prose 'There may be multiple chroots; they are stored in $CHROOTDIR.' + echo + prose 'Each chroot is named; the default is configured with $CHROOT.' + echo + prose 'Each named chroot has a master clean copy (named `root`), and any + number of other named copies; the copy used by default is the + current username (or $SUDO_USER, or `copy` if root).' + echo + prose 'The full path to the chroot copy is "$CHROOTDIR/$CHROOT/$COPY", + unless the copy name is manually specified as an absolute path, + in which case, that path is used.' + echo + prose 'The current settings for the above varibles are:' + printf ' CHROOTDIR : %s\n' "${CHROOTDIR:-$(_ 'ERROR: NO SETTING')}" + printf ' CHROOT : %s\n' "${CHROOT:-$(_ 'ERROR: NO SETTING')}" + printf ' COPY : %s\n' "$COPY" + printf ' rootdir : %s\n' "${rootdir:-$(_ 'ERROR')}" + printf ' copydir : %s\n' "${copydir:-$(_ 'ERROR')}" + echo + prose 'If the chroot, or copy does not exist, it will be created + automatically. A chroot by default contains the packages in the + group "base-devel", and any packages named in $CHROOTEXTRAPKG. + Unless the `-C` or `-M` flags are used, the configuration files + that this program installs are the stock versions supplied in the + packages, not the versions from your host system. Other tools + (such as libremakepkg) may alter the configuration.' + echo + prose 'This command will make the following configuration changes in the + chroot:' + bullet 'overwrite `/etc/libretools.d/chroot.conf`' + bullet 'overwrite `/etc/pacman.d/mirrorlist`' + bullet 'set `CacheDir` in `/etc/pacman.conf`' + prose 'If a new `pacman.conf` is inserted with the `-C` flag, the change + is made after the file is copied in; the `-C` flag doesn'\''t + stop the change from being effective.' + echo + prose 'Creating a copy, deleting a copy, or syncing a copy can be fairly + slow; but are very fast if $CHROOTDIR is on a btrfs partition.' + echo + print 'Options:' + flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' + flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the copy to use' + flag '-N' 'Disable networking in the chroot' + flag "-C <$(_ FILE)>" 'Copy this file to `$copydir/etc/pacman.conf`' + flag "-M <$(_ FILE)>" 'Copy this file to `$copydir/etc/makepkg.conf`' + flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only' + echo + print 'Commands:' + print ' Create/copy/delete:' + flag 'noop|make' 'Do not do anything, but still creates the chroot + copy if it does not exist' + flag 'sync' 'Sync the copy with the clean (`root`) copy' + flag 'delete' 'Delete the chroot copy' + print ' Dealing with packages:' + flag "install-file $(_ FILES...)" 'Like `pacman -U FILES...`' + flag "install-name $(_ NAMES...)" 'Like `pacman -S NAMES...`' + flag 'update' 'Like `pacman -Syu`' + flag 'clean-pkgs' 'Remove all packages from the chroot copy that + are not in base-devel, $CHROOTEXTRAPKG, or named + as a dependency in the file `/build/PKGBUILD` in + the chroot copy' + print ' Other:' + flag "run $(_ CMD...)" 'Run CMD in the chroot copy' + flag 'enter' 'Enter an interactive shell in the chroot copy' + flag 'clean-repo' 'Clean /repo in the chroot copy' + flag 'help' 'Show this message' +} +readonly commands=( + noop make sync delete + install-file install-name update clean-pkgs + run enter clean-repo help +) + +# set $rootdir and $copydir; blank them on error +calculate_directories() { + # Don't assume that CHROOTDIR or CHROOT are set, + # but assume that COPY is set. + local rootdir copydir + + if [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then + rootdir="${CHROOTDIR}/${CHROOT}/root" + else + rootdir='' + fi + + if [[ ${COPY:0:1} = / ]]; then + copydir=$COPY + elif [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then + copydir="${CHROOTDIR}/${CHROOT}/${COPY}" + else + copydir='' + fi + + declare -p rootdir + declare -p copydir +} + +arch_nspawn_flags=() +sysd_nspawn_flags=() +arch-nspawn() { + local copydir=$1; shift + set +u # if an array is empty, it counts as unbound + "$_arch_nspawn" "${arch_nspawn_flags[@]}" "$copydir" "${sysd_nspawn_flags[@]}" -- "$@" + set -u +} + +# Globals: $CHROOTDIR, $CHROOT, $COPY, $rootdir and $copydir +main() { + COPY=$LIBREUSER + [[ $COPY != root ]] || COPY=copy + + local mode=enter + while getopts 'n:l:NC:M:w:r:' opt; do + case $opt in + n) CHROOT=$OPTARG;; + l) COPY=$OPTARG;; + N) sysd_nspawn_flags+=(--private-network);; + C|M) arch_nspawn_flags+=(-$opt "$OPTARG");; + w) sysd_nspawn_flags+=("--bind=$OPTARG");; + r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");; + *) usage >/dev/stderr; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + error "Must specify a command" + usage >/dev/stderr + return 1 + fi + mode=$1 + if ! in_array "$mode" "${commands[@]}"; then + error "Unrecognized command: %s" "$mode" + usage >/dev/stderr + return 1 + fi + shift + + if [[ $mode == help ]]; then + usage + return 0 + fi + + check_vars chroot CHROOTDIR CHROOT + eval "$(calculate_directories)" + + readonly LIBREUSER LIBREHOME + readonly CHROOTDIR CHROOT COPY + readonly rootdir copydir + readonly mode + + ######################################################################## + + if (( EUID )); then + error "This program must be run as root." + return 1 + fi + + umask 0022 + + # Keep this lock as long as we are running + # Note that '9' is the same FD number as in mkarchroot et al. + lock 9 "$copydir.lock" \ + "Waiting for existing lock on chroot copy to be released: [%s]" "$COPY" + + if [[ ! -d $rootdir ]]; then + msg "Creating 'root' copy for chroot [%s]" "$CHROOT" + set +u # if an array is empty, it counts as unbound + "$_mkarchroot" "${arch_nspawn_flags[@]}" "$rootdir" base-devel + set -u + make_empty_repo "$rootdir" + fi + + if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then + msg "Syncing copy [%s] with root copy" "$COPY" + _makechrootpkg sync_chroot "$CHROOTDIR/$CHROOT" "$COPY" + fi + + mkdir -p "$copydir/etc/libretools.d" + { + if [[ -n ${CHROOTEXTRAPKG[*]:-} ]]; then + declare -p CHROOTEXTRAPKG | sed -r 's/declare( -.)* //' + else + printf 'CHROOTEXTRAPKG=()\n' + fi + } > "$copydir"/etc/libretools.d/chroot.conf + + if [[ $mode != delete ]]; then + # "touch" the chroot first + # this will + # - overwrite \`/etc/pacman.d/mirrorlist'" + # - set \`CacheDir' in \`/etc/pacman.conf'" + # - apply -C or -M flags + arch-nspawn "$copydir" true + arch_nspawn_flags=() # XXX dirty hack, don't apply -C or -M again + fi + + ######################################################################## + + case "$mode" in + # Creat/copy/delete + noop|make|sync) :;; + delete) + if [[ -d $copydir ]]; then + _makechrootpkg delete_chroot "$copydir" "$COPY" + fi + ;; + + # Dealing with packages + install-file) + _makechrootpkg install_packages "$copydir" "$@" + chroot_add_to_local_repo "$copydir" "$@" + ;; + install-name) + arch-nspawn "$copydir" pacman -Sy "$@" + ;; + update) + arch-nspawn "$copydir" pacman -Syu --noconfirm + ;; + clean-pkgs) + trap "rm -f '$copydir'/bin/chcleanup '$copydir'/chrootexec" EXIT + install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup" + printf '%s\n' \ + '#!/bin/bash' \ + 'mkdir -p /build' \ + 'cd /build' \ + '/bin/chcleanup' \ + > "$copydir/chrootexec" + chmod 755 "$copydir/chrootexec" + arch-nspawn "$copydir" /chrootexec + ;; + + # Other + run) + arch-nspawn "$copydir" "$@" + ;; + enter) + arch-nspawn "$copydir" bash + ;; + clean-repo) + rm -rf "${copydir}"/repo/* + make_empty_repo "$copydir" + ;; + esac +} + +main "$@" diff --git a/src/chroot-tools/libremakepkg b/src/chroot-tools/libremakepkg new file mode 100755 index 0000000..a59315b --- /dev/null +++ b/src/chroot-tools/libremakepkg @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +set -euE +# libremakepkg + +# Copyright 2010-2011 Nicolás Reynolds +# Copyright 2011 Joshua Ismael Haase Hernández +# Copyright 2012-2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. $(librelib conf) +. $(librelib messages) +. $(librelib chroot/makechrootpkg.sh) + +shopt -s nullglob +umask 0022 + +# Global variables: +readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false) +NONET=true # can be changed with the -N flag +# {SRC,LOG,PKG}DEST set at runtime by makepkg.conf +# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf +# LIBREUSER, LIBREHOME are set by conf.sh + +# Hooks ######################################################################## + +hook_pre_build=(:) +hook_post_build=(:) +hook_check_pkgbuild=(:) +hook_check_pkg=(:) +. $(librelib chroot/hooks-chcleanup.sh) +. $(librelib chroot/hooks-check.sh) +. $(librelib chroot/hooks-distcc.sh) + +# Boring/mundane functions ##################################################### + +# Usage: exit_copy $copydir $src_owner +# End immediately, but copy log files out +exit_copy() { + local copydir=$1 + local src_owner=$2 + if ! $INCHROOT; then + msg "Copying log and package files out of the chroot..." + move_products "$copydir" "$src_owner" + fi +} + +# Usage; run_hook $hookname $args... +run_hook() { + local hookname=$1; shift + local hookvar="hook_${hookname}[@]" + local fails=() + msg "Running hook: %s" "$hookname" + for hook in "${!hookvar}"; do + msg2 'hook: %s' "$hook" + "$hook" "$@" || { error "result: %s" $?; fails+=("$hook"); } + done + if [[ ${#fails[@]} -gt 0 ]]; then + error "Failure(s) in %s: %s" "$hookname" "${fails[*]}" + return 1 + fi + return 0 +} + +# Usage: add_to_local_repo $copydir $pkgfiles... +add_to_local_repo() { + local copydir=$1; shift + mkdir -p "$copydir/repo" + local pkgfile + for pkgfile in "$@"; do + cp "$pkgfile" "$copydir/repo" + pushd "$copydir/repo" >/dev/null + repo-add repo.db.tar.gz "${pkgfile##*/}" + popd >/dev/null + done +} + +build() ( + local copydir=$1; shift + local cmd=(/chrootbuild "$@") + + run_hook pre_build "$copydir" + trap "run_hook post_build '$copydir'" EXIT + + local netflag='' + if $INCHROOT; then + ! $NONET || netflag='-n' + unshare $netflag -- "${cmd[@]}" + else + ! $NONET || netflag='-N' + librechroot $netflag \ + -r "$PWD:/startdir_host" \ + -r "$SRCDEST:/srcdest_host" \ + -l "$copydir" \ + run "${cmd[@]}" + fi +) + +# The main program ############################################################# + +usage() { + print "Usage: %s [options] [-- makepkg args]" "${0##*/}" + print 'This program will build your package.' + echo + prose 'If run from outside of a chroot, command will make the following + configuration changes in the chroot:' + bullet 'whatever changes `librechroot` makes.' + bullet 'set `PKGDEST` and `SRCDEST` in `/etc/makepkg.conf`' + bullet 'set `PACKAGER` in `/etc/makepkg.conf` to reflect the value + outside of the chroot.' + bullet '(maybe) delete `/build/.makepkg.conf`' + bullet '(maybe) delete `/build/.ssh/config`' + prose 'If run from inside of a chroot, this command will:' + bullet '(maybe) delete `~/.makepkg.conf`' + bullet '(maybe) delete `~/.ssh/config`' + prose 'The above "maybe"s happen as part of the workarounds to make + distcc work in a network-less environment. They will happen if + both `socat` and `distcc` are installed in the chroot.' + echo + prose 'The `-n` and `-l` options behave identically to librechroot, see + the documentation there.' + echo + print 'Options:' + flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' + flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use' + flag '-N' "Don't disable networking during build() and + package(). PLEASE don't use this unless you + have a special reason, its use is a violation + of Parabola policy." + flag '-R' 'Repackage contents of the package without rebuilding' + flag '-h' 'Show this message' +} + +# Convenience method for use in option parsing +err_chflag() { + local flag=$1 + error 'The -%s flag does not make sense inside of a chroot' "$flag" + return 1 +} + +main() { + # Initial variable values ############################################## + local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER") + local makepkg_args=(-s --noconfirm -L) + local repack=false + local chroot='' + + # Parse command line options ########################################### + while getopts 'n:l:NRh' flag ; do + case "${flag}" in + n) if $INCHROOT; then err_chflag "$flag"; else chroot=$OPTARG; fi;; + l) if $INCHROOT; then err_chflag "$flag"; else copy=$OPTARG; fi;; + N) NONET=false;; + R) repack=true; makepkg_args+=(-R);; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + # Pass all arguments after -- right to makepkg + makepkg_args+=("$@") + + # Resolve the chroot path ############################################## + local copydir + if $INCHROOT; then + copydir='/' + else + load_files chroot + check_vars chroot CHROOTDIR CHROOT + [[ -z ${chroot} ]] || CHROOT=$chroot + if [[ ${copy:0:1} = / ]]; then + copydir=$copy + else + copydir="${CHROOTDIR}/${CHROOT}/${copy}" + fi + unset CHROOTDIR CHROOTEXTRAPKG + fi + unset chroot + + # Quick sanity check ################################################### + + if (( EUID )); then + error "This program must be run as root" + exit 1 + fi + + if [[ ! -f PKGBUILD ]]; then + # This is the message used by makepkg + error "PKGBUILD does not exist." + exit 1 + fi + + # Load makepkg configuration ########################################### + # Note that all of these are globals + SRCDEST="$(get_conf_makepkg SRCDEST "$PWD")" + PKGDEST="$(get_conf_makepkg PKGDEST "$PWD")" + LOGDEST="$(get_conf_makepkg LOGDEST "$PWD")" + mkdir -p "$SRCDEST" "$PKGDEST" "$LOGDEST" + MAKEFLAGS="$(get_conf_makepkg MAKEFLAGS '')" + PACKAGER="$(get_conf_makepkg PACKAGER '')" + + # OK, we are starting now ############################################## + + if $INCHROOT; then + lock 9 "/build/.lock" \ + "Waiting for existing lock on build directory to be released" + else + # Obtain a lock on the chroot + lock 9 "$copydir.lock" \ + "Waiting for existing lock on chroot copy to be released: [%s]" "$copy" + # Create the chroot if it does not exist + librechroot -n "$CHROOT" -l "$copy" make + fi + + # Set target CARCH + # note that we waited until after locking/creating the chroot to do this + export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_conf_makepkg CARCH)" + + # Pre-build + run_hook check_pkgbuild + download_sources "$copydir" "$LIBREUSER" + prepare_chroot "$copydir" "$LIBREHOME" "$repack" false + clean_chroot "$copydir" + + # Build + trap "exit_copy '$copydir' '$LIBREUSER'" EXIT + warning 'Entering build...' + build "$copydir" "${makepkg_args[@]}" + # Post-build + warning 'Entering hook check_pkg...' + run_hook check_pkg + warning 'Entering add_to_local_repo ...' + add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* +} + +main "$@" diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch new file mode 100644 index 0000000..f5b8ed7 --- /dev/null +++ b/src/chroot-tools/makechrootpkg.sh.patch @@ -0,0 +1,303 @@ +--- makechrootpkg.sh.in 2013-09-08 23:01:20.000000000 -0400 ++++ makechrootpkg.sh.ugly 2013-09-09 15:43:06.000000000 -0400 +@@ -12,6 +12,7 @@ + + shopt -s nullglob + ++init_variables() { + _makepkg_args=(-s --noconfirm -L --holdver) + makepkg_args=("${_makepkg_args[@]}") + repack=false +@@ -26,9 +27,10 @@ + declare -i ret=0 + + copy=$USER +-[[ -n $SUDO_USER ]] && copy=$SUDO_USER ++[[ -n ${SUDO_USER:-} ]] && copy=$SUDO_USER + [[ -z "$copy" || $copy = root ]] && copy=copy + src_owner=${SUDO_USER:-$USER} ++} + + usage() { + echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]" +@@ -62,6 +64,7 @@ + exit 1 + } + ++parse_options_init() { + while getopts 'hcur:I:l:nT' arg; do + case "$arg" in + h) usage ;; +@@ -86,9 +89,6 @@ + [[ ! -d $chrootdir ]] && die "No chroot dir defined, or invalid path '%s'" "$passeddir" + [[ ! -d $chrootdir/root ]] && die "Missing chroot dir root directory. Try using: mkarchroot %s/root base-devel" "$chrootdir" + +-# Detect chrootdir filesystem type +-chroottype=$(stat -f -c %T "$chrootdir") +- + if [[ ${copy:0:1} = / ]]; then + copydir=$copy + else +@@ -103,30 +103,47 @@ + repack=true + fi + +-if [[ -n $SUDO_USER ]]; then ++if [[ -n ${SUDO_USER:-} ]]; then + USER_HOME=$(eval echo ~$SUDO_USER) + else + USER_HOME=$HOME + fi ++} + + # {{{ functions ++# Usage: load_vars $makepkg_conf ++# Globals: ++# - SRCDEST ++# - LOGDEST ++# - PKGDEST ++# - MAKEFLAGS ++# - PACKAGER + load_vars() { + local makepkg_conf="$1" var + + [[ -f $makepkg_conf ]] || return 1 + + for var in {SRC,PKG,LOG}DEST MAKEFLAGS PACKAGER; do +- [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf") ++ [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf") + done + + return 0 + } + +-create_chroot() { +- # Lock the chroot we want to use. We'll keep this lock until we exit. +- lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" ++# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir> ++sync_chroot() { ++ local chrootdir=$1 ++ local copy=$2 ++ local copydir='' ++ if [[ ${copy:0:1} = / ]]; then ++ copydir=$copy ++ else ++ copydir="$chrootdir/$copy" ++ fi ++ ++ # Detect chrootdir filesystem type ++ local chroottype=$(stat -f -c %T "$chrootdir") + +- if [[ ! -d $copydir ]] || $clean_first; then + # Get a read lock on the root chroot to make + # sure we don't clone a half-updated chroot + slock 8 "$chrootdir/root.lock" "Locking clean chroot" +@@ -147,10 +164,15 @@ + + # Drop the read lock again + lock_close 8 +- fi + } + +-clean_temporary() { ++# Usage: delete_chroot $copydir [$copy] ++delete_chroot() { ++ local copydir=$1 ++ local copy=${2:-$copydir} ++ # Detect chrootdir filesystem type ++ local chroottype=$(stat -f -c %T "$copydir") ++ + stat_busy "Removing temporary copy [%s]" "$copy" + if [[ "$chroottype" == btrfs ]]; then + btrfs subvolume delete "$copydir" >/dev/null || +@@ -166,9 +188,14 @@ + stat_done + } + ++# Usage: install_packages $copydir $pkgs... + install_packages() { ++ local copydir=$1 ++ local install_pkgs=("${@:2}") ++ declare -i ret=0 + local pkgname + ++ local install_pkg + for install_pkg in "${install_pkgs[@]}"; do + pkgname="${install_pkg##*/}" + cp "$install_pkg" "$copydir/$pkgname" +@@ -179,11 +206,19 @@ + rm "$copydir/$pkgname" + done + +- # If there is no PKGBUILD we are done +- [[ -f PKGBUILD ]] || exit $ret ++ return $ret + } + ++# Usage: prepare_chroot $copydir $HOME $repack $run_namcap ++# Globals: ++# - MAKEFLAGS ++# - PACKAGER + prepare_chroot() { ++ local copydir=$1 ++ local USER_HOME=$2 ++ local repack=$3 ++ local run_namcap=$4 ++ + $repack || rm -rf "$copydir/build" + + mkdir -p "$copydir/build" +@@ -217,12 +252,12 @@ + + chown -R nobody "$copydir"/{build,pkgdest,logdest,srcdest,startdir} + +- if [[ -n $MAKEFLAGS ]]; then ++ if [[ -n ${MAKEFLAGS:-} ]]; then + sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf" + echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf" + fi + +- if [[ -n $PACKAGER ]]; then ++ if [[ -n ${PACKAGER:-} ]]; then + sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf" + echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf" + fi +@@ -235,6 +270,14 @@ + chmod 440 "$copydir/etc/sudoers.d/nobody-pacman" + fi + ++ if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then ++ cat >> "$copydir/etc/pacman.conf" <<EOF ++[repo] ++SigLevel = Optional TrustAll ++Server = file:///repo ++EOF ++ fi ++ + # This is a little gross, but this way the script is recreated every time in the + # working copy + printf $'#!/bin/bash\n%s\n_chrootbuild %q "$@"' "$(declare -f _chrootbuild)" \ +@@ -242,13 +285,19 @@ + chmod +x "$copydir/chrootbuild" + } + ++# Usage: download_sources $copydir $src_owner ++# Globals: ++# - SRCDEST + download_sources() { ++ local copydir=$1 ++ local src_owner=$2 ++ + local builddir="$(mktemp -d)" + chmod 1777 "$builddir" + + # Ensure sources are downloaded +- if [[ -n $SUDO_USER ]]; then +- sudo -u $SUDO_USER env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ ++ if [[ $USER != $src_owner ]]; then ++ sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ + makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o + else + ( export SRCDEST BUILDDIR="$builddir" +@@ -258,7 +307,7 @@ + (( $? != 0 )) && die "Could not download sources." + + # Clean up garbage from verifysource +- rm -rf $builddir ++ rm -rf "$builddir" + } + + _chrootbuild() { +@@ -295,6 +344,7 @@ + + # Safety check + if [[ ! -w PKGBUILD ]]; then ++ # XXX: internationalize this message + echo "Can't write to PKGBUILD!" + exit 1 + fi +@@ -312,12 +362,24 @@ + exit 0 + } + ++# Usage: move_products $copydir $owner ++# Globals: ++# - PKGDEST ++# - LOGDEST + move_products() { ++ local copydir=$1 ++ local src_owner=$2 ++ ++ local pkgfile + for pkgfile in "$copydir"/pkgdest/*; do + chown "$src_owner" "$pkgfile" + mv "$pkgfile" "$PKGDEST" ++ if [[ $PKGDEST != $PWD ]]; then ++ ln -sf "$PKGDEST/${pkgfile##*/}" . ++ fi + done + ++ local l + for l in "$copydir"/logdest/*; do + chown "$src_owner" "$l" + mv "$l" "$LOGDEST" +@@ -325,6 +387,10 @@ + } + # }}} + ++main() { ++init_variables ++parse_options_init ++ + umask 0022 + + load_vars /etc/makepkg.conf +@@ -335,27 +401,37 @@ + [[ -d $SRCDEST ]] || SRCDEST=$PWD + [[ -d $LOGDEST ]] || LOGDEST=$PWD + +-create_chroot ++# Lock the chroot we want to use. We'll keep this lock until we exit. ++lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" ++ ++if [[ ! -d $copydir ]] || $clean_first; then ++ sync_chroot "$chrootdir" "$copy" ++fi + + $update_first && arch-nspawn "$copydir" pacman -Syu --noconfirm + +-[[ -n ${install_pkgs[*]} ]] && install_packages ++if [[ -n ${install_pkgs[*]:-} ]]; then ++ install_packages "$copydir" "${install_pkgs[@]}" ++ ret=$? ++ # If there is no PKGBUILD we have done ++ [[ -f PKGBUILD ]] || exit $ret ++fi + +-prepare_chroot ++prepare_chroot "$copydir" "$USER_HOME" "$repack" + +-download_sources ++download_sources "$copydir" "$src_owner" + + if arch-nspawn "$copydir" \ + --bind-ro="$PWD:/startdir_host" \ + --bind-ro="$SRCDEST:/srcdest_host" \ + /chrootbuild "${makepkg_args[@]}" + then +- move_products ++ move_products "$copydir" "$src_owner" + else + (( ret += 1 )) + fi + +-$temp_chroot && clean_temporary ++$temp_chroot && delete_chroot "$copydir" "$copy" + + if (( ret != 0 )); then + if $temp_chroot; then +@@ -366,3 +442,4 @@ + else + true + fi ++} diff --git a/src/devtools/.gitignore b/src/devtools/.gitignore new file mode 100644 index 0000000..097fcde --- /dev/null +++ b/src/devtools/.gitignore @@ -0,0 +1,3 @@ +* +!Makefile +!.gitignore diff --git a/src/devtools/Makefile b/src/devtools/Makefile new file mode 100644 index 0000000..3fc5d70 --- /dev/null +++ b/src/devtools/Makefile @@ -0,0 +1,8 @@ +progs = checkpkg find-libdeps finddeps lddd +copy_files = $(addsuffix .in,$(progs)) +install_files = $(DESTDIR)$(bindir)/find-libprovides +include ../../common.mk + +$(DESTDIR)$(bindir)/find-libprovides: + install -d $(@D) + ln -sf find-libdeps $@ diff --git a/src/fullpkg/Makefile b/src/fullpkg/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/fullpkg/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/fullpkg/fullpkg b/src/fullpkg/fullpkg new file mode 100755 index 0000000..1d4388c --- /dev/null +++ b/src/fullpkg/fullpkg @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# set -x # uncomment for debug +# Builds packages from ABS recursively. It tries to find dependencies that +# aren't built or need update and then makepkg them in order. + +usage() { + + echo "cd to a dir containing a PKGBUILD and run:" + echo "$0 [build_dir]" + echo "" + echo "This script will check dependencies, build them if possible " + echo "and stage the packages on it's repo." + echo "" + echo "OPTIONS:" + echo " -h : this message." + echo "" + echo "Wrapper for \`fullpkg-find' and \`fullpkg-build'" + echo "" + exit 1 + +} + +while getopts 'h' arg; do + case "$arg" in + h) usage ;; + esac +done + +shift $(( OPTIND - 1 )) + +build_dir="${1:-$(mktemp -d /tmp/fullpkg.XXXXXX)}" +fullpkg-find "$build_dir" && fullpkg-build -N "$build_dir" + +exit 0 diff --git a/src/fullpkg/fullpkg-build b/src/fullpkg/fullpkg-build new file mode 100755 index 0000000..1771d83 --- /dev/null +++ b/src/fullpkg/fullpkg-build @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# set -x # uncomment for debug +# Builds packages from ABS recursively. It tries to find dependencies that +# aren't built or need update and then makepkg them in order. + +# TODO move __build to chroot + +. libremessages +. $(librelib conf.sh) +load_files makepkg +load_files libretools +check_vars libretools FULLBUILDCMD || exit 1 +# The following variables are actually optional +#check_vars libretools HOOKPKGBUILDMOD HOOKLOCALRELEASE || exit 1 + +## List packages on log that are on status +## usage: list_pkgs <status> <message> +# +## status: nonfree, built, failed, unstaged +list_pkgs() { + msg="$2" + local pkgs=($(grep "$1:" $build_dir/log)) && { + msg "$2" + echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 + } +} + +## Check all build_dir, fails if one PKGBUILD is nonfree +check_nonfree() { + find "$build_dir" -name PKGBUILD \ + -exec pkgbuild-check-nonfree {} + + if [ "$?" -eq 15 ]; then + error "Some PKGBUILD have nonfree problems" + exit 15 + fi + +} + +# Removes a package from the buildorder +# $1 package name +# $2 buildorder file +remove_buildorder() { + grep -Evw "${1}" ${2} > ${2}2 + mv -f ${2}2 ${2} + + return $? +} + +succesfull_build() { + + if [ "$RUN" != "$FULLBUILDCMD" ]; then + return 0 # Custom command or download sources + fi + + if source .INFO && [ -n "$repo" ]; then + + if [ ! -z "$HOOKLOCALRELEASE" ]; then + "$HOOKLOCALRELEASE" "$repo" + fi + + msg "Updating pacman db and packages" + sudo pacman -Sy || true + + fi + + echo "built:$(basename $PWD)" >>$build_dir/log +} + +build_description() { + list_pkgs "nonfree" "Those packages contain nonfree deps:" + list_pkgs "built" "Those packages were built and staged:" + list_pkgs "failed" "Those packages failed to build:" + list_pkgs "unstaged" "Those packages couldn't be staged (missing reponame):" +} + +__build() { + pushd ${build_dir} >/dev/null + + build_packages=($(sort -gr $buildorder | cut -d: -f2)) # greater levels must be built first + + while [ ${#build_packages[@]} -ge 1 ]; do + + pushd "$build_dir/${build_packages[0]}" >/dev/null + + if [ -n "${HOOKPKGBUILDMOD}" ]; then + ${HOOKPKGBUILDMOD} || true + fi + + eval "$RUN"; r=$? + + case $r in + + 0) succesfull_build ;; + + *) error "There were errors while trying to build the package." + echo "failed:$(basename $PWD)" >>$build_dir/log + ;; + esac + + remove_buildorder "${build_packages[0]}" $buildorder || true + +# which is next package? + build_packages=($(sort -gr $buildorder | cut -d: -f2)) + popd > /dev/null + done + + popd >/dev/null +} + +# End inmediately but print a useful message +trap_exit() { + error "$@" + warning "Leftover files left on $build_dir" + mv .BUILDORDER BUILDORDER + exit 1 +} + +# Trap signals from makepkg +set -E +trap 'trap_exit "(fullpkg-build) TERM signal caught. Exiting..."' TERM HUP QUIT +trap 'trap_exit "(fullpkg-build) Aborted by user! Exiting..."' INT +trap 'trap_exit "(fullpkg-build) An unknown error has occurred. Exiting..."' ERR + +CLEANUP="false" +CHECKNONFREE="true" +RUN="$FULLBUILDCMD" +MESSAGE="Building packages" + +usage() { + + echo "" + echo "$(basename $0) [options] <build_dir>" + echo "" + echo "Builds packages from build_dir, create a build_dir using:" + echo "'fullpkg-find <build_dir>'" + echo "" + echo "If no <build_dir> is specified, it uses the current directory." + echo "" + echo "OPTIONS:" + echo " -h : this message." + echo " -c : clean <build_dir> on succesfull build" + echo " -N : don't check for freedom issues." #Also made by fullpkg-find + echo " -r \"command\" : use this instead of \"$FULLBUILDCMD\"." + echo " -g : get sources for building packages on build_dir." + echo "" + exit 1 + +} + +while getopts 'hNr:g' arg; do + case $arg in + h) usage ;; + c) CLEAN ;; + N) CHECKNONFREE="false" ;; + r) RUN="$OPTARG" + MESSAGE="Executing custom action";; + g) RUN='makepkg -g > /dev/null' + MESSAGE="Downloading packages";; + esac +done + +shift $(( OPTIND - 1 )) +build_dir="${1:-`pwd`}" +buildorder="${build_dir}/BUILDORDER" + +if [ ! -e "$buildorder" ]; then + error "This is not a build_dir. Make one using fullpkg." + usage +else +# backup BUILDORDER + cp "$buildorder" "$build_dir/.BUILDORDER" +fi + +if "$CHECKNONFREE"; then + check_nonfree +fi + +msg "$MESSAGE" +__build + +if [ "$RUN" != "$FULLBUILDCMD" ]; then + # Used for downloading or custom command + mv "$build_dir/.BUILDORDER" "$buildorder" + exit 0 +elif "$CLEANUP"; then + find "$build_dir" -mindepth 1 -delete +fi + +build_description + +plain "Test packages on and if they work fine librerelease." + +exit 0 diff --git a/src/fullpkg/fullpkg-find b/src/fullpkg/fullpkg-find new file mode 100755 index 0000000..a927782 --- /dev/null +++ b/src/fullpkg/fullpkg-find @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# set -x # uncomment for debug +# Builds packages from ABS recursively. It tries to find dependencies that +# aren't built or need update and then makepkg them in order. + +# TODO: fullpkg-find should find packages wich depend on the +# package to be build, so we can avoid "missing $name.so errors" + +# Get repo name. Asumes ${ABSROOT}/repo/package/PKGBUILD +guess_repo() { + basename $(dirname $(pwd)) +} + +# This function is stolen from makechrootpkg. +# That probably has some copyright/licensing implications. +copy_files() { + + local copydir="$build_dir/${pkgbase:-${pkgname[0]}}" + mkdir -p "$copydir" + + # Copy PKGBUILD and sources + cp PKGBUILD "$copydir" + ( + load_PKGBUILD + for file in "${source[@]}"; do + file="${file%%::*}" + file="${file##*://*/}" + if [[ -f $file ]]; then + cp "$file" "$copydir/" + elif [[ -f $SRCDEST/$file ]]; then + cp "$SRCDEST/$file" "$copydir/" + fi + done + + # Find all changelog and install files, even inside functions + for i in 'changelog' 'install'; do + while read -r file; do + # evaluate any bash variables used + eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\" + [[ -f $file ]] && cp "$file" "$copydir" + done < <(sed -n "s/^[[:space:]]*$i=//p" PKGBUILD) + done + ) +} + +# Checks ABSROOT and look for target pkg deps. Adds them if not built or outdated. +find_deps() { +# Check this level + load_PKGBUILD + + local repo="${repo:-$(guess_repo)}" + local pkgbase="${pkgbase:-${pkgname[0]}}" + + if ! pkgbuild-check-nonfree > /dev/null 2> /dev/null; then + if [ "$?" -eq 15 ]; then + error "pkgbase" has nonfree issues + return 15 + fi + fi + + # Checking any package built, since otherwise e.g. kdebase would + # be always considered outdated: there is no package built named kdebase. + # TODO: maybe check for the package requested in case of recursive calls, + # instead of the first one listed? + if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then + exit 0 # pkg is built and updated + fi + +# greater levels are built first + echo "${LEVEL}:${pkgbase}" >>"$build_dir/BUILDORDER" +# PKGBUILD is already there + if [ -d "${build_dir}/${pkgbase}" ]; then + exit 0 +# Copy dir to build_dir + else + copy_files + +# to identify repo later + echo "repo=$repo" > "${build_dir}/${pkgbase}/.INFO" + fi + +# current package plus a space for every level + msg2 "%${LEVEL}s${pkgbase}-$(get_full_version)" + +## Check next levels + declare -i next_level=$LEVEL+1 + +# All deps in separate line, only once, without version. + deps=($(echo "${depends[@]} ${makedepends[@]}" | \ + sed "s/[=<>]\+[^ ]\+//g" | \ + tr ' ' "\n" | \ + sort -u)) + + for _dep in ${deps[@]}; do + + local found=false + # May fail, e.g. since abslibre-mips64el doesn't include + # arch=any packages. + local pkgdir=$(toru -p ${_dep}) || true + + if [ -n "$pkgdir" -a -d "${pkgdir}" ]; then + found=true + + pushd "${pkgdir}" > /dev/null +# runs itself on dep's PKGBUILD dir + $0 -l ${next_level} ${build_dir} || return $? + popd > /dev/null + fi + + if ! (( found )); then + echo "dep_not_found:$_dep" >>$build_dir/log + fi + + done + +## End variable block + + unset next_level dir +} + +. libremessages +. $(librelib conf.sh) +load_files makepkg + +LEVEL=0 +MAXLEVEL=20 +CLEANFIRST='false' +UPDATEDB='true' + +usage() { + + echo "" + echo "cd to a dir containing a PKGBUILD and run:" + echo "$(basename $0) [options] <build_dir>" + echo "" + echo "This script will create a build_dir for recursive building" + echo "it tries to find dependencies that aren't built or need update." + echo "" + echo "If no <build_dir> is specified, the script works on a tempdir" + echo "" + echo "OPTIONS:" + echo " -h : this message." + echo " -A <absroot> : use this ABSROOT." + echo " -c : clean <build_dir> before working." + echo " -m <max_level> : check deps until this level" + echo " -n : don't update pacman db." + echo "" + exit 1 + +} + +while getopts 'hA:l:cmn' arg; do + case "$arg" in + h) usage ;; + A) ABSROOT="$OPTARG" ;; + l) LEVEL="$OPTARG" ;; # hidden option to know dep level. + c) CLEANFIRST='true' ;; + m) MAXLEVEL="$OPTARG" ;; + n) UPDATEDB='false' ;; + esac +done + +if [ ! -r PKGBUILD ]; then + error "This directory doesnt contain a PKGBUILD" + usage +fi + +shift $(( OPTIND - 1 )) +build_dir="${1}" + +if [ "$LEVEL" -eq 0 ]; then + + build_dir="${1:-$(mktemp -d /tmp/fullpkg.XXXXXX)}" + + if [ ! -d "$build_dir" ]; then + mkdir -p "$build_dir" + elif "$CLEANFIRST"; then + # Erase files already in dir + msg "Cleaning up files in dir" + find "$build_dir" -mindepth 1 -delete + fi + + if "$UPDATEDB"; then + msg "Updating pacman db" + sudo pacman -Sy --noconfirm || true + fi + +# make files for log and buildorder + touch "${build_dir}"/{log,BUILDORDER} + buildorder="${build_dir}/BUILDORDER" + + msg "Checking dependencies" +fi + +find_deps + +exit 0 diff --git a/src/is_built b/src/is_built new file mode 100755 index 0000000..80d0719 --- /dev/null +++ b/src/is_built @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +cmd=${0##*/} +usage() { + echo "Usage: $cmd [-h] pkgname [pkgver]" + echo + echo "Detect if a given package version is already in repos" + echo "Assuming you want greater or equal." + echo + echo "Example usage: $cmd 'pcre' '20'" + echo + echo "Exit status:" + echo " 0: The package is built" + echo " 1: The package has not built" + echo " >1: There was an error" +} + +while getopts 'h' arg; do + case $arg in + h) usage; exit 0 ;; + *) usage >&2; exit 2 ;; + esac +done +if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then + usage >&2 + exit 2 +fi + +pkg=${1} +ver=${2:-0} +pver=$(LC_ALL=C pacman -Sddp --print-format '%v' "${pkg}" 2>/dev/null) + +# if pacman fails or returns nothing +r=$? + +result=$(vercmp "${pver}" "${ver}") +# result: +# -1 : pver < ver +# 0 : pver = ver +# 1 : pver > ver + +if [[ $result -ge 0 ]] && [[ $r -eq 0 ]]; then + exit 0 +else + exit 1 +fi diff --git a/src/lib/.gitignore b/src/lib/.gitignore new file mode 100644 index 0000000..9a0c402 --- /dev/null +++ b/src/lib/.gitignore @@ -0,0 +1,3 @@ +common.sh +common.sh.in +common.sh.top diff --git a/src/lib/Makefile b/src/lib/Makefile new file mode 100644 index 0000000..45fd330 --- /dev/null +++ b/src/lib/Makefile @@ -0,0 +1,30 @@ +copy_files = common.sh.in +libexecs = $(filter-out librelib,$(wildcard libre*)) +# include common.sh in libs explicitly, because it might not exist yet +# when the wildcard is performed +libs = $(sort $(wildcard *.sh) common.sh) +include ../../common.mk + +# Build ############################################################## + +common.sh: %: %.in %.top Makefile + @echo "GEN $@" + @{ \ + cat '$*.top' && \ + echo && \ + sed -r -e '/encoding problem/d;/LANG=/d' -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' '$*.in' && \ + echo && \ + cat '$*.bottom' && \ + :; } > '$@' + +# Translate ########################################################## + +pot: libreblacklist.pot common.sh.pot librelib.pot + +libreblacklist.pot: libreblacklist + { \ + sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | sed -r -e 's/^# (.*)/msgid "\1"/' -e 's/^[0-9]*$$/#. embedded usage text\n#: $<:&/'; \ + sed -rn '/print /{ =; s/\s*print "([^"]*)".*/msgid "\1"/p; }' $< | sed 's/^[0-9]*$$/#. print\n#: $<:&/' ; \ + } | sed 's/^msgid .*/&\nmsgstr ""\n/' > $@ +common.sh.pot: common.sh + xgettext --omit-header -i --from-code=UTF-8 -L shell -o $@ $< diff --git a/src/lib/common.sh.bottom b/src/lib/common.sh.bottom new file mode 100644 index 0000000..e133fad --- /dev/null +++ b/src/lib/common.sh.bottom @@ -0,0 +1 @@ +fi diff --git a/src/lib/common.sh.top b/src/lib/common.sh.top new file mode 100644 index 0000000..054301b --- /dev/null +++ b/src/lib/common.sh.top @@ -0,0 +1,26 @@ +#!/bin/bash # non-executable, but put this there as a hint to text editors +# This may be included with or without `set -euE` + +# This file is included by libremessages. +# You should probably use libremessages instead of this. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +if [[ -z ${_INCLUDE_COMMON_SH:-} ]]; then +_INCLUDE_COMMON_SH=true + +export TEXTDOMAIN='libretools' +export TEXTDOMAINDIR='/usr/share/locale' + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi diff --git a/src/lib/conf.sh b/src/lib/conf.sh new file mode 100644 index 0000000..f96af26 --- /dev/null +++ b/src/lib/conf.sh @@ -0,0 +1,190 @@ +#!/bin/bash # non-executable, but put this there as a hint to text editors +# This may be included with or without `set -euE` + +# Copyright (c) 2012-2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +LIBREUSER="${SUDO_USER:-$USER}" +if [[ $LIBREUSER == $USER ]]; then + LIBREHOME=$HOME +else + LIBREHOME="$(eval echo ~$LIBREUSER)" +fi +if [[ -z ${XDG_CONFIG_HOME:-} ]]; then + export XDG_CONFIG_HOME="${LIBREHOME}/.config" +fi +if [[ -z ${XDG_CACHE_HOME:-} ]]; then + export XDG_CACHE_HOME="${LIBREHOME}/.cache" +fi + +# Generic functions ############################################################ + +# Usage: list_files $slug +# Lists the configuration files to be considered for $slug. +# Later files should take precedence over earlier files. +list_files() { + local slug=$1 + case $slug in + abs) + echo /etc/$slug.conf + echo "$LIBREHOME/.$slug.conf" + ;; + makepkg) + if [[ ${MAKEPKG_CONF:-} != /etc/$slug.conf && -r ${MAKEPKG_CONF:-} ]]; then + echo "$MAKEPKG_CONF" + else + echo /etc/$slug.conf + echo "$LIBREHOME/.$slug.conf" + fi + ;; + libretools) + echo /etc/$slug.conf + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + *) + echo /etc/libretools.d/$slug.conf + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + esac +} + +# Usage: list_envvars $slug +# Lists the environmental variables that take precidence over the configuration +# files for $slug. +list_envvars() { + local slug=$1 + case $slug in + makepkg) + printf '%s\n' \ + PKGDEST SRCDEST SRCPKGDEST LOGDEST \ + BUILDDIR \ + PKGEXT SRCEXT \ + GPGKEY PACKAGER + ;; + *) :;; + esac +} + +# Usage: load_files $slug +# Loads the configuration files for $slug in the proper order. +load_files() { + local slug=$1 + local var + local file + + # Save the existing versions at _VARNAME + for var in $(list_envvars $slug); do + [[ -n ${!var:-} ]] && eval "_$var=\${$var}" + done + + # Load the files + for file in $(list_files $slug); do + if [[ -r $file ]]; then + . "$file" || return 1 + fi + done + + # Restore the _SAVED versions + for var in $(list_envvars $slug); do + eval "$var=\${_$var:-\${$var:-}}" + done +} + +# Usage: check_vars $slug VAR1 VAR2... +# Check whether the variables listed are properly set. +# If not, it prints a message saying to set them in the configuration file(s) +# for $slug. +check_vars() { + local slug=$1; shift + + local ret=0 + + local VAR + for VAR in "$@"; do + if [[ -z ${!VAR:-} ]]; then + type print &>/dev/null || . libremessages + if [[ $(list_files $slug|wc -l) -gt 1 ]]; then + print "Configure '%s' in one of:" "$VAR" + list_files $slug | sed 's/./ -> &/' + else + print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)" + fi + ret=1 + fi + done >&2 + + if [[ $ret != 0 ]]; then + return 1 + fi +} + +# makepkg configuration ######################################################## + +# Usage: get_conf_makepkg <var_name> <default_value> +get_conf_makepkg() ( + set +euE + local setting=$1 + local default=$2 + load_files makepkg + printf '%s\n' "${!setting:-${default}}" +) + +set_conf_makepkg() { + local key=$1 + local val=$2 + local file + for file in $(list_files makepkg|tac); do + if [[ -w $file ]]; then + sed -i "/^\s*$key=/d" "$file" + printf '%s=%q\n' "$key" "$val" >> "$file" + return 0 + fi + done + return 1 +} + +# PKGBUILD (not configuration, per se) ######################################### + +unset_PKGBUILD() { + # This routine is based primarily off of the PKGBUILD(5) man-page, + # version 4.1.2, dated 2013-06-18. + + # From the "OPTIONS AND DIRECTIVES" section (in order of mention) + unset -v pkgname pkgver + unset -f pkgver + unset -v pkgrel pkgdesc epoch url license install changelog source + unset -v noextract md5sums sha{1,256,384,512}sums groups arch backup + unset -v depends makedepends checkdepends optdepends conflicts provides + unset -v replaces options + + # From the "PACKAGING FUNCTIONS" section (in order of mention) + unset -f package prepare build check + + # From the "PACKAGE SPLITTING" section + unset -f $(declare -f|sed -n 's/^\(package_\S*\) ()\s*$/\1/p') + unset -v pkgbase + + # These are used by the `librefetch` program + unset -v mksource mknoextract mkmd5sums mksha{1,256,384,512}sums + unset -v mkdepends + unset -f mksource +} + +load_PKGBUILD() { + local file=${1:-./PKGBUILD} + unset_PKGBUILD + CARCH="$(get_conf_makepkg CARCH "`uname -m`")" + . "$file" +} diff --git a/src/lib/libreblacklist b/src/lib/libreblacklist new file mode 100755 index 0000000..5db1a3f --- /dev/null +++ b/src/lib/libreblacklist @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` +# When run directly, it does `set -euE` + +# Copyright (c) 2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# make sure XDG_CACHE_HOME is set +. $(librelib conf) + +# Usage: blacklist-normalize <$file +# Normalizes the syntax of the blacklist on stdin. +blacklist-normalize() { + sed -e 's/^[^:]*$/&::/' -e 's/^[^:]*:[^:]*$/&:/' +} + +# Usage: blacklist-cat +# Prints the blacklist. +# Uses the cache, but downloads it if it doesn't exist. Also normalizes the blacklist for easier parsing. +blacklist-cat() { + local file="$XDG_CACHE_HOME/libretools/blacklist.txt" + if ! [[ -e $file ]]; then + # exit on failure, whether set -e or not + blacklist-update || return $? + fi + blacklist-normalize < "$file" +} + +# Usage: blacklist-update +# Updates (or creates) the cached copy of the blacklist. +blacklist-update() ( + . libremessages + load_files libretools || return 1 + check_vars libretools BLACKLIST || return 1 + + local remote_blacklist="$BLACKLIST" + local local_blacklist="$XDG_CACHE_HOME/libretools/blacklist.txt" + + stat_busy "Downloading blacklist of proprietary software packages" + + mkdir -p "${local_blacklist%/*}" + if wget -N -q -O "${local_blacklist}.part" "$remote_blacklist" 2>/dev/null; then + stat_done + mv -f "${local_blacklist}.part" "$local_blacklist" + else + stat_done + rm "${local_blacklist}.part" + if [[ -e "$local_blacklist" ]]; then + warning "Using local copy of blacklist" + else + error "Download failed, exiting" + return 1 + fi + + fi +) + +# Usage: blacklist-cat | blacklist-lookup $pkgname +# Filters to obtain the line for $pkgname from the blacklist on stdin. +# Exits successfully whether a line is found or not. +blacklist-lookup() { + local pkg=$1 + # we accept that $pkg contains no regex-nes + blacklist-normalize | grep "^$pkg:" || true +} + +# Usage: blacklist-cat | blacklist-get-pkg +# Prints only the package name field of the blacklist line(s) on stdin. +blacklist-get-pkg() { + blacklist-normalize | cut -d: -f1 +} + +# Usage: blacklist-cat | blacklist-get-rep +# Prints only the replacement package field of the blacklist line(s) on stdin. +blacklist-get-rep() { + blacklist-normalize | cut -d: -f2 +} + +# Usage: blacklist-cat | blacklist-get-reason +# Prints only the reason field of the blacklist line(s) on stdin. +blacklist-get-reason() { + blacklist-normalize | cut -d: -f3- +} + +if [[ "${0##*/}" == libreblacklist ]]; then + set -euE + usage-outside() { + sed -n '/^# Usage:/,/()/p' "$0" | + tr '\n' '\r' | sed 's/\s*()\s*[{(]/\n/g' + } + # The output format of this is: + # - The first line is "Usage:" + # - The second line is a brief description + # - The last line is the command name (prefixed with "blacklist-") + # - The in-between lines are the extended description. + usage-inside() { + sed 's/\r/\n/g'<<<"$1"|sed -e '/^$/d' -e 's/^# //' + } + usage() { + . $(librelib messages) + if [[ $# -eq 0 ]]; then + print "Usage: %s [-h] COMMAND [ARGUMENTS]" "${0##*/}" + print "Tool for working with the nonfree software blacklist" + echo + print "Commands:" + usage-outside | while read -r sec; do sec="$(usage-inside "$sec")" + cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p') + desc="$(_ "$(sed -n 2p <<<"$sec")")" + flag "$cmd" "${desc//blacklist-/${0##*/} }" + done + else + usage-outside | while read -r sec; do sec="$(usage-inside "$sec")" + cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p') + if [[ "$cmd" == "$1" ]]; then + <<<"$sec" sed '$d' | + while read -r line; do print "$line"; done | + sed "s/blacklist-/${0##*/} /g" | + fmt -us + return 0 + fi + done + fi + } + + if [[ $# -eq 0 ]]; then + usage >/dev/stderr + exit 1 + fi + _blacklist_cmd=$1 + shift + if [[ $_blacklist_cmd == -h ]]; then + usage "$@" + else + "blacklist-$_blacklist_cmd" "$@" + fi +fi diff --git a/src/lib/librelib b/src/lib/librelib new file mode 100755 index 0000000..2dc9314 --- /dev/null +++ b/src/lib/librelib @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# Copyright (c) 2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +default_libdir=/usr/lib/libretools + +if ! type gettext &>/dev/null; then + gettext() { echo "$@"; } +fi + +print() { + mesg=$1 + shift + printf -- "$(gettext "$mesg")\n" "$@" +} + +prose() { + print "$@" | fmt -su +} + +cmd=${0##*/} +usage() { + . libremessages + print 'Usage: . $(%s LIBRARY)' "$cmd" + print "Finds a shell library file" + echo + prose "While some libraries can be sourced just by their name because + they are installed in PATH (like libremessages), some are not + installed there (like conf.sh), so a path must be given. + Hardcoding that path is the way of the dark side." + echo + prose 'By default, it looks for the files in `%s`, but this can be + changed with the environmental variable LIBRETOOLS_LIBDIR.' "$default_libdir" + echo + print "Example usage:" + printf ' . $(%s conf.sh)\n' "$cmd" +} + +main() { + if [[ $# != 1 ]]; then + usage >&2 + return 2 + fi + if [[ $1 == '-h' ]]; then + usage + return 0; + fi + + if [[ -z $LIBRETOOLS_LIBDIR ]]; then + export LIBRETOOLS_LIBDIR=$default_libdir + fi + + lib=$1 + lib=${lib#libre} + lib=${lib%.sh} + + for file in ${lib} libre${lib} ${lib}.sh libre${lib}.sh; do + if [[ -f "$LIBRETOOLS_LIBDIR/$file" ]]; then + printf '%s\n' "$LIBRETOOLS_LIBDIR/$file" + return 0; + fi + done + print '%s: could not find library: %s' "$cmd" "$lib" >> /dev/stderr + return 1 +} + +main "$@" diff --git a/src/lib/libremessages b/src/lib/libremessages new file mode 100755 index 0000000..c6d08e2 --- /dev/null +++ b/src/lib/libremessages @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` +# When run directly, it does `set -euE` + +# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (c) 2011 by Joshua Haase <hahj87@gmail.com> +# Copyright (c) 2012-2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +################################################################################ +# Inherit most functions from devtools # +################################################################################ + +. $(librelib common.sh) + +################################################################################ +# Own functions # +################################################################################ + +panic() { + echo "$(_ 'panic: malformed call to internal function')" >&2 + exit 1 +} + +# Usage: print MESG ARG1 ARG2... +# Like printf, but gettext-aware, and prints a trailing newline +print() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$1")" + shift + printf -- "$mesg\n" "$@" +} + +# Do HTML-style whitespace collapsing on standard IO. It considers newline, +# tab, and space to be whitespace. +_html_whitespace_collapse() { + [[ $# == 0 ]] || panic + tr '\n' ' ' | sed -r -e 's/\t/ /g' -e 's/ +/ /g' +} + + +# Usage: prose MESG +# Do HTML-style whitespace collapsing on the first argument, translate it +# (gettext), then word-wrap it to 75 columns. +# This is useful for printing a paragraph of prose in --help text. +prose() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(_html_whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u +} + +# Usage: bullet MESG +# Like prose, but print a bullet "-" before the first line, and indent the +# remaining lines. +bullet() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(_html_whitespace_collapse <<<"$1")")"; shift + # Wrap the text to 71 columns; 75 (the default) minus a 4 column indent + printf -- "$mesg" "$@" | fmt -u -w 71 | sed -e '1s/^/ - /' -e '2,$s/^/ /' +} + +# Usage: flag FLAG DESCRIPTION +# Print a flag and description formatted for --help text. +# ex: flag '-C <FILE>' 'Use this file instead of pacman.conf' +# The description is fed through gettext, the flag is not, so if part of the +# flag needs to be translated, you must do that yourself: +# ex: flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf' +# If you want to line-break the description in the source, so it isn't +# crazy-long, feel free, it is reflowed/wrapped the same way as prose and +# bullet. +flag() { + [[ $# == 2 ]] || panic + local n=' +' + local flag=$1 + local desc="$(_ "$(_html_whitespace_collapse <<<"$2")")" + + declare -i indent=13 + while [[ $indent -le ${#flag} ]]; do + indent=$((indent+8)) + done + + local lines + IFS=$n lines=($(fmt -u -w $((73-indent)) <<<"$desc")) + local line + for line in "${lines[@]}"; do + printf " %-${indent}s %s\n" "$flag" "$line" + flag='' + done +} + +# Usage: term_title This will be the term title +# Sets the terminal title +term_title() { + [[ $# -ge 1 ]] || panic + local fmt='' + case "$TERM" in + screen|tmux) fmt='\ek%s\e\\';; + xterm*|rxvt*) fmt='\e]0;%s\a';; + esac + printf "$fmt" "$*" +} + +################################################################################ +# Run one of the defined functions if invoked directly # +################################################################################ + +if [[ "${0##*/}" == libremessages ]]; then + set -euE + _libremessages_cmd=$1 + shift + "$_libremessages_cmd" "$@" +fi diff --git a/src/librefetch/Makefile b/src/librefetch/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/librefetch/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/librefetch/librefetch b/src/librefetch/librefetch new file mode 100755 index 0000000..086a5e9 --- /dev/null +++ b/src/librefetch/librefetch @@ -0,0 +1,331 @@ +#!/usr/bin/env bash +# librefetch +# +# Copyright 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. $(librelib conf.sh) +. libremessages + +declare -r tempdir="$(mktemp -d --tmpdir ${0##*/}.XXXXXXXXXXX)" +cleanup() { rm -rf -- "$tempdir"; } +trap cleanup EXIT + +cmd=${0##*/} +usage() { + print "Usage: %s [options] <source-url> [<output-file>]" "$cmd" + print "Usage: %s -[g|P|V|h]" "$cmd" + print "Downloads or creates a liberated source tarball." + echo + print "The default mode is to create <output-file>, first by trying download" + print "mode, then create mode." + echo + print "If <output-file> isn't specified, it defaults to the non-directory" + print "part of <source-url>, in the current directory." + echo + print "In download mode, the glob '*://' is stripped from the beginning of" + print "<source-url>, and the resulting path is attempted to be downloaded" + print "from the configured mirror." + echo + print "In create mode, it looks at a build script, and uses that to create" + print "the source tarball. <source-url> is ignored, except that it is used" + print "to set the default value of <output-file>." + echo + print "The default build script is 'PKGBUILD', or 'SRCBUILD' if it exists." + echo + print "Unrecognized options are passed straight to makepkg." + echo + print "Example usage:" + print ' $ %s libre://mypackage-1.0.tar.gz' "$cmd" + echo + print "Options:" + print " Settings:" + print " -C Force create mode (don't download)" + print " -D Force download mode (don't create)" + print " -p <file> Use an alternate build script (instead of 'PKGBUILD')" + print " If an SRCBUILD exists in the same directory, it is used" + print " instead" + print " Alternate modes:" + print " -g, --geninteg Generage integrity checks for source files" + print " -P, --print Print the effective build script (SRCBUILD)" + print " -V, --version Show version information" + print " -h, --help Show this message" +} + +version() { + print "librefetch (libretools) beta 4" + echo + print "Copyright (C) 2013 Luke Shumaksr <lukeshu@sbcglobal.net>" + print "This is free software; see the source for copying conditions." + print "There is NO WARRANTY, to the extent permitted by law." +} + +main() { + BUILDFILE="$(readlink -m PKGBUILD)" + makepkg_opts=() + extra_opts=() + mode=download-create + parse_options "$@" + + # Mode: version, help ################################################## + + if [[ $mode =~ version ]]; then + version + return 0 + fi + if [[ $mode =~ help ]]; then + usage + return 0 + fi + + ######################################################################## + + local BUILDFILEDIR="${BUILDFILE%/*}" + if [[ -f "${BUILDFILEDIR}/SRCBUILD" ]]; then + BUILDFILE="${BUILDFILEDIR}/SRCBUILD" + srcbuild="$(modified_srcbuild "$BUILDFILE")" + else + srcbuild="$(modified_pkgbuild "$BUILDFILE")" + fi + makepkg="$(modified_makepkg "$(which makepkg)")" + + # Mode: checksums ###################################################### + + if [[ $mode =~ checksums ]]; then + if [[ ${#extra_opts[@]} != 0 ]]; then + print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >> /dev/stderr + usage >> /dev/stderr + return 1 + fi + "$makepkg" "${makepkg_opts[@]}" -g -p "$srcbuild" | + case ${BUILDFILE##*/} in + PKGBUILD) sed -e 's/^[a-z]/mk&/' -e 's/^\s/ &/';; + SRCBUILD) cat;; + esac + return 0 + fi + + # Mode: print ########################################################## + + if [[ $mode =~ print ]]; then + if [[ ${#extra_opts[@]} != 0 ]]; then + print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >> /dev/stderr + usage >> /dev/stderr + return 1 + fi + cat "$srcbuild" + return 0 + fi + + ######################################################################## + + local src dst + case ${#extra_opts[@]} in + 1) + src="${extra_opts[0]#*://}" + dst="$(readlink -m -- "${src##*/}")" + ;; + 2) + src="${extra_opts[0]#*://}" + dst="$(readlink -m -- "${extra_opts[1]}")" + ;; + *) + print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >> /dev/stderr + usage >> /dev/stderr + return 1 + esac + + # Mode: download ####################################################### + + if [[ $mode =~ download ]]; then + load_files librefetch + check_vars librefetch MIRROR DOWNLOADER || return 1 + + local url="${MIRROR}/${src}" + + local dlcmd="${DOWNLOADER}" + dlcmd="${dlcmd//\%o/\"$dst\"}" + dlcmd="${dlcmd//\%u/\"$url\"}" + { eval "$dlcmd"; } >> /dev/stderr && return 0 + fi + + # Mode: create ######################################################### + + if [[ $mode =~ create ]]; then + PKGEXT=${dst##*/} + export PKGEXT=${PKGEXT%.part} + export PKGDEST=${dst%/*} + export pkg_file=$dst + + cd "$BUILDFILEDIR" + "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >> /dev/stderr || return $? + fi +} + +# sets the variables BUILDFILE, makepkg_opts, extra_opts, mode +parse_options() { + # Detect makepkg options that take a second argument + local makepkg_orig="$(which "${MAKEPKG:-makepkg}")" + local makepkg_opt2long=($("${makepkg_orig}" -h | sed -rn 's/\s*(--\S*) <.*/\1/p')) + local makepkg_opt2short=($("${makepkg_orig}" -h | sed -rn 's/\s*(-.) <.*/\1/p')) + + local opt + local have_opt + while [[ $# -gt 0 ]]; do + arg=$1 + have_opt=false + if in_array "${arg%%=*}" "${makepkg_opt2long[@]}"; then + opt="${arg#*=}" + arg="${arg%%=*}" + have_opt=true + fi + if in_array "${arg}" "${makepkg_opt2short[@]}"; then + shift + opt=$1 + have_opt=true + fi + case "$arg" in + -C) mode=create;; + -D) mode=download;; + -g|--geninteg) mode=checksums;; + -P|--print) mode=print;; + -p) BUILDFILE="$(readlink -m -- "$opt")";; + -V|--version) mode=version;; + -h|--help) mode=help;; + -*) + makepkg_opts+=("$arg") + $have_opt && makepkg_opts+=("$opt") + ;; + --) shift; break;; + *) extra_opts+=("$arg");; + esac + shift + done + extra_opts+=("$@") +} + +# Modify makepkg ############################################################### + +# an ERE +makepkg_modify=' +/create_package\(\) \{/,/^\}$/ { + /pkg_file=/d # allow us to set pkg_file + s/"?\$\{comp_files\[@\]\}"?// # do not include .{PKGINFO,INSTALL,CHANGELOG} + s/bsdtar /&--format=ustar / # ustar, not pax + s/create_signature .*/&; return $?/ # do not procede to create symlinks +} + +/tidy_install\(\) \{/,/^\}$/ { + /for .*PURGE_TARGETS/itidy_install_purge + /for .*PURGE_TARGETS/,/done/d + /^\}$/ifind . -exec touch --date="1990-01-01 0:0:0 +0" {} + +} + +s|srcdir=.*|&-libre| +s|pkgdirbase=.*|&-libre| +s|check_build_status$|:| +' + +tidy_install_purge() { + local pt + for pt in "${PURGE_TARGETS[@]}"; do + if [[ ${pt} = "${pt%/}" ]]; then + if [[ ${pt} = "${pt//\/}" ]]; then + find . ! -type d -name "${pt}" -exec rm -f -- '{}' + + else + rm -f "${pt}" + fi + else + if [[ ${pt%/} = "${pt//\/}" ]]; then + find . -type d -name "${pt%/}" -exec rm -rf -- '{}' + + else + rm -rf "${pt}" + fi + fi + done +} + +modified_makepkg() { + local makepkg_orig=$1 + local makepkg_mine="$tempdir/makepkg" + { + echo '#!/bin/bash' + declare -f tidy_install_purge + sed -r "$makepkg_modify" < "$makepkg_orig" + } > "$makepkg_mine" + chmod 755 "$makepkg_mine" + printf "%s\n" "$makepkg_mine" +} + +# Modify PKGBUILD ############################################################## + +# a string to be appended +pkgbuild_append=' +# do not do split packages +if [[ ${#pkgname[@]} -gt 1 ]]; then + if [[ -n $pkgbase ]]; then + pkgname=("$pkgbase") + else + pkgname=("$pkgname") + fi +fi + +# copy source variables +source=("${mksource[@]}") +noextract=("${mknoextract[@]}") +md5sums=("${mkmd5sums[@]}") +sha1sums=("${mksha1sums[@]}") +sha256sums=("${mksha256sums[@]}") +sha384sums=("${mksha384sums[@]}") +sha512sums=("${mksha512sums[@]}") + +depends=() +checkdepends=() +makedepends=("${mkdepends[@]}") + +#### +options+=(!strip docs libtool emptydirs !zipman purge !upx) +PURGE_TARGETS+=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) + +#### +if ! declare -f mksource >/dev/null; then + mksource() { :; } +fi +prepare() { :; } +build() { mksource; } +check() { :; } +package() { cp -a "$srcdir"/*/ "$pkgdir/"; } +' + +modified_pkgbuild() { + local pkgbuild=$1 + local srcbuild="$tempdir/SRCBUILD" + printf '%s' "$pkgbuild_append" | cat "$pkgbuild" - > "$srcbuild" + printf '%s\n' "$srcbuild" +} + + +# Modify SRCBUILD ############################################################## + +modified_srcbuild() { + local orig=$1 + local new="$tempdir/SRCBUILD" + sed -e '/PKGDEST=/d' -e '/PKGEXT=/d' < "$orig" > "$new" + printf '%s\n' "$new" +} + +main "$@" diff --git a/src/librefetch/librefetch.8.ronn b/src/librefetch/librefetch.8.ronn new file mode 100644 index 0000000..7fa15d4 --- /dev/null +++ b/src/librefetch/librefetch.8.ronn @@ -0,0 +1,162 @@ +librefetch(8) -- downloads or creates a liberated source tarball +================================================================ + +## SYNOPSIS + +`librefetch` [options] <source-url> [<output-file>]<br> +`librefetch` -[g|V|h] + +## DESCRIPTION + +`librefetch` is a program to streamline creation of custom source +tarballs for `PKGBUILD(5)` files. + +To automatically use `librefetch` to download or create a source +tarball, you can add `libre://FILENAME.tar.gz` to the source array in +your `PKGBUILD`. This works because a post-install script for the +package adds `librefetch` as a download agent for `libre://` to +`makepkg.conf`. Because of this, it is almost never necessary to call +`librefetch` manually. + +There are 7 modes: + + * `download-create`: The default mode. First try `download` mode, + then `create` mode. + * `download`: Download the tarball from the configured mirror. + * `create`: Create the tarball from a `PKGBUILD`/`SRCBUILD`. + * `checksums`: Generate integrity checks for source files. + * `print`: Print the effective build script. + * `version`: Print `librefetch` version information. + * `help`: Print `librefetch` usage information. + +## OPTIONS + + * `-C`: Force `create` mode (don't download) + * `-D`: Force `download` mode (don't create) + * `-p` <file>: Use an alternate build script for `create` mode + (instead of `PKGBUILD`). If an `SRCBUILD` file exists in the same + directory, it is used instead. + * `-g` | `--geninteg`: Use `checksums` mode: Generate integrity + checks for source files. + * `-P` | `--print`: Use `print` mode: print the effective build script. + * `-V` | `--version`: Use `version` mode: Show version information. + * `-h` | `--help`: Use `help` mode: Show useage information. + +## CREATE MODE + +The principle of `create` mode is that a special `PKGBUILD(5)`, called +a `SRCBUILD(5)`, installs source files to `$pkgdir`, and the resulting +"package" is then used as a source tarball. The `SRCBUILD` exists in +the same directory as the `PKGBUILD`. It can be created manually, or +generated on-the-fly from the `PKGBUILD`. Extra steps are taken to +ensure that as long as the same directory contents go in, an identical +tarball will come out--the checksum of the file should not change +based on when it is built or who builds it. + +The `SRCBUILD` is either created, or sanitized if it already exists, +then fed to a modified version of `makepkg(8)`. + +The purpose of the modified `makepkg` is so that the resulting tarball +doesn't contain package metadata, doesn't end with a .pkg file +extension, and always produces an identicle tarball. + +When this documentation speaks of a file being modified, it is a +temporary copy of the file that is modified, your original file will +remain intact. + +## SRCBUILD GENERATION + +As explained in the `CREATE MODE` section, in `create` mode, this +program generates an `SRCBUILD` file. For debugging purposes, this +file can be printed instead of executed with `print` mode. + +### PRE-EXISTING SRCBUILD + +The use of `SRCBUILD` files pre-dates `librefetch`. By convention, +they set `PKGDEST` and `PKGEXT` in `package()` in order to modify the +behavior of `makepkg`. Because a modified version of `makepkg` is +used, this interferes with the correct behavior. To compensate for +this, lines containing "`PKGDEST=`" or "`PKGEXT=`" are deleted from +the `SRCBUILD`. + +The general idea is that `build()` makes any modifications to the +source, then `package()` copies it from `$srcdir` to `$pkgdir`. + +### SRCBUILD FROM PKGBUILD + +Possibly more elegant than having a separate `SRCBUILD` file is having +an `mksource()` function in the main `PKGBUILD`. This results in less +boilerplate and fewer files to edit. + +Note that this only happens if a file named `SRCBUILD` doesn't already +exist; when migrating a package from a manually created `SRCBUILD` to +this method, the `SRCBUILD` must be deleted (or renamed) for this to +work. + +The dynamically created `SRCBUILD` is created by copying `PKGBUILD` to +a temorary file, then re-setting variables and re-defining functions. +Following is a table of the translations. + + Variables + source = mksource + noextract = mknoextract + *sums = mk*sums (md5, sha1, sha256, sha384, sha512) + depends = <empty> + checkdepends = <empty> + makedepends = mkdepends + Functions + prepare() { :; } + build() { mksource; } + check() { :; } + package() { cp -a "$srcdir"/*/ "$pkgdir/"; } + +The `mksource()` function does not need to be defined. If it isn't +defined, then no transformations will be made to the source between it +being extracted to `$srcdir` and copied to `$pkgdir`. + +In summary: + + * Set `mksource=()` and `mkmd5sums=(`) to act as `source=(`) and + `md5sums=()` + * Declare a `mksource()` function to make modifications to the + source, if nescessary. + +Other changes: + + * `pkgname` is set to `pkgbase`, or the first element of the + `pkgname` array. + * `options=()` is set have `makepkg` avoid making changes to + `$pkgdir`. The exact change is: + + options+=(!strip docs libtool emptydirs !zipman purge !upx) + + * `PURGE_TARGETS=()` has vcs directories added to it: + + PURGE_TARGETS+=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) + +### MAKEPKG MODIFICATIONS + +The following modifications are made to makepkg: + + * Allow us to manipulate the output file (`$pkg_file`) + * Do not include metadata in the output file (`${comp_files[@]}`) + * Force 'ustar' tar format, don't allow it to upgrade to 'pax' to + store extended file attributes. + * Don't symlink the resulting file into the current directory. + * `PURGE_TARGETS` interprets an item as a directory if it ends with a + slash ("/"). + * Timestamps in `$pkgdir` are reset to "1990-01-01 0:0:0 +0", so that + the resulting tarball will be the same, regardless of when it was + created. + * append `-libre` to `$srcdir` + * append `-libre` to `$pkgbasedir` (which becomes `$pkgdir`) + * Don't check if the package has already been built. + +## CONFIGURATION + +See `librefetch.conf(5)` for details on configuring librefetch using +the `librefetch.conf` file. + +## SEE ALSO + +librefetch.conf(5), makepkg(8), PKGBUILD(5), SRCBUILD(5) diff --git a/src/librefetch/librefetch.conf b/src/librefetch/librefetch.conf new file mode 100644 index 0000000..40d2078 --- /dev/null +++ b/src/librefetch/librefetch.conf @@ -0,0 +1,2 @@ +MIRROR='https://repo.parabolagnulinux.org/sources/' +DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' diff --git a/src/librefetch/librefetch.conf.5.ronn b/src/librefetch/librefetch.conf.5.ronn new file mode 100644 index 0000000..3d80ab5 --- /dev/null +++ b/src/librefetch/librefetch.conf.5.ronn @@ -0,0 +1,36 @@ +librefetch.conf(5) -- librefetch configuration file +=================================================== + +## SYNOPSIS + +`/etc/libretools.d/librefetch.conf`, `~/.config/libretools/librefetch.conf` + +## DESCRIPTION + +Configuration for librefetch is stored in `librefetch.conf`. The +several places it looks for the file are: + + * `/etc/libretools.d/librefetch.conf` + * `$XDG_CONFIG_HOME/libretools/librefetch.conf` + +The later files take precidence over earlier files, but earlier files +are loaded, so that later files only need to set the values they want +to override. + +If `$XDG_CONFIG_HOME` is not set, a default value is set: + + * if `$SUDO_USER` is set: `$(eval echo ~$SUDO_USER)/.config` + * else: `$HOME/.config` + +## OPTIONS + + * `MIRROR='https://repo.parabolagnulinux.org/sources/'`: + The location to download pre-built source tarball in download + mode. + * `DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u'`: + The HTTP client to use when downloading pre-built source tarballs + in download mode. + +## SEE ALSO + +librefetch(8) diff --git a/src/libregit/Makefile b/src/libregit/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/libregit/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/libregit/libregit b/src/libregit/libregit new file mode 100755 index 0000000..8687d2f --- /dev/null +++ b/src/libregit/libregit @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Copyright (c) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +cd_safe() { + if ! cd "$1"; then + error "Failed to change to directory %s" "$1" + plain "Aborting..." + exit 1 + fi +} + +download_git() { + if [[ ! -d "$dir/.git" ]] ; then + msg2 "Cloning %s %s repo..." "${repo}" "git" + if ! git clone "$repo" "$dir"; then + error "Failure while downloading %s %s repo" "${repo}" "git" + plain "Aborting..." + exit 1 + fi + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$repo" != "$(git config --get remote.origin.url)" ]] ; then + error "%s is not a clone of %s" "$dir" "$repo" + plain "Aborting..." + exit 1 + fi + msg2 "Updating %s %s repo..." "${repo}" "git" + if ! git pull origin "$ref"; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${repo}" "git" + fi + fi +} + +usage() { + print 'Usage: %s repo ref dir' "${0##*/}" + echo + print "Clones or pulls from the git URL 'repo', and checks out the git ref" + print "'ref' to the directory 'dir'." + echo + print "It does safety checks, figures out whether to clone or pull, and other" + print "helpful things. This exists because the same 'download_git' function" + print "from makepkg was being copied again and again." +} + +main() { + [[ $# == 3 ]] || { usage >&2; return 1; } + repo=$1 + ref=$2 + dir=$3 + + [[ -d "${dir%/*}" ]] || mkdir -p "${dir%/*}" + download_git +} + +main "$@" diff --git a/src/libretools.conf b/src/libretools.conf new file mode 100644 index 0000000..593aed6 --- /dev/null +++ b/src/libretools.conf @@ -0,0 +1,83 @@ +#!/bin/bash # non-executable, but put this there as a hint to text editors + +################################################################################ +# misc # +################################################################################ + +# The dir where you work on +WORKDIR=/home/$LIBREUSER/packages + +## Blacklist URL +BLACKLIST=https://repo.parabolagnulinux.org/docs/blacklist.txt + +## Diff tool (vimdiff, gvimdiff, meld, etc) +## Used by `aur`, `diff-unfree` +DIFFTOOL=`which kdiff3 meld gvimdiff vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q'` + +## The repos you'll be packaging for +## Used by `toru`, `createworkdir` +# Tip: As early repos take precedence on $REPOS loops, you can use this as +# inverted order of precedence. Put testing repos first so fullpkg find new +# PKGBUILDs first, for instance. Toru-path uses reverse order to enforce repo +# precedence on the path cache (the last path added replaces the rest) +REPOS=('core' 'libre' 'extra' 'community' 'libre-testing' 'social' 'sugar' 'pcr' 'java') + +## The architectures you'll be packaging for +## Used by `librestage` +ARCHES=('x86_64' 'i686' 'mips64el') + +## ABSLibre +#ABSLIBREGIT=http://projects.parabolagnulinux.org/abslibre.git +ABSLIBREGIT=ssh://git@projects.parabolagnulinux.org:1863/srv/git/abslibre.git + +################################################################################ +# librerelease # +################################################################################ + +## Where to upload packages to +# Don't change unless you know what you're doing and you won't screw +# anything ;) +REPODEST=repo@repo:/srv/http/repo/public +## Assumes something similar in your .ssh/config: +# Host repo +# Port 1863 +# HostName repo.parabolagnulinux.org + +## These are run before and after uploading packages +HOOKPRERELEASE="ssh -fN ${REPODEST%%:*}" +HOOKPOSTRELEASE="sudo librechroot clean-repo" + +################################################################################ +# fullpkg/treepkg # +################################################################################ + +# Note: fullpkg accepts HOOK* settings not being set, treepkg requires them to +# be set. + +# NOTE: fullpkg ONLY +#HOOKPKGBUILDMOD="mips-add" + +# Run a command before running FULLBUILDCMD, usually to cleanup uneeded packages +# Note: Recent versions of libremakepkg run chcleanup for you. +# NOTE: treepkg ONLY +HOOKPREBUILD=":" +#HOOKPREBUILD="chcleanup" + +## Uncomment one of those or make one of your choice +# Normal +FULLBUILDCMD="sudo libremakepkg" +# Cross compiling +#FULLBUILDCMD="sudo libremakepkg -n cross-compile-chroot" +# Don't use a chroot +#FULLBUILDCMD="makepkg -sL --noconfirm" + +# Locally release the package or any other action after running FULLBUILDCMD +# succesfully. When run, it is given a repository name as a single argument. +HOOKLOCALRELEASE="librestage" +#HOOKLOCALRELEASE="mipsrelease" + +################################################################################ +# toru # +################################################################################ + +TORUPATH=/var/lib/libretools/toru diff --git a/src/mips64el-tools/Makefile b/src/mips64el-tools/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/mips64el-tools/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/mips64el-tools/add-mips64el b/src/mips64el-tools/add-mips64el new file mode 100755 index 0000000..17b167b --- /dev/null +++ b/src/mips64el-tools/add-mips64el @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Change all arch array that aren't any or mips64el already + +find -name 'PKGBUILD' -exec sed -i "s/^\(arch=([^)anym]\+\))/\1 'mips64el')/" '{}' \; + +exit $? diff --git a/src/mips64el-tools/librebasebuilder b/src/mips64el-tools/librebasebuilder new file mode 100755 index 0000000..6b8c8bb --- /dev/null +++ b/src/mips64el-tools/librebasebuilder @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +set -e + +# TODO: + +# - make it more configurable + +# - compare the result with previous base images + +for arg in "$@" ; do + case "$arg" in + -h|--h|--he|--hel|--help|-\?) + echo 'Usage: librebasebuilder + +Make a base tarball named parabola-mips64el-DATE.tar.bz2. + +This script must be run as root.' >&2 + exit 0 + ;; + esac +done + +[[ "$UID" != "0" ]] && { + echo "This script must be run as root." >&2 + exit 1 +} + +tempdir=/home/chroot/base +outdir=$(pwd) + +archroot -n $tempdir mkinitcpio base sudo parted nano zile vi ed openssh + +cd $tempdir + +# Don't list mtjm's DNS servers. +cat > etc/resolv.conf <<EOF +# +# /etc/resolv.conf +# + +#search <yourdomain.tld> +#nameserver <ip> + +# End of file +EOF + +# From pacman-mirrorlist-libre-20120307-1. Remember to escape dollars. +cat > etc/pacman.d/mirrorlist <<EOF +# Parabola GNU/Linux - Last Updated: Wed Mar 7 17:33:36 GMT 2012 + +# Atlanta, GA, USA +# Responsible: belos +Server = http://parabola.techno-geeks.org/\$repo/os/\$arch + +# Nuremberg, Germany +# Responsible: johkra +Server = http://parabolaweb.eu/\$repo/os/\$arch + +# UK +# Responsible: Parabola Project +Server = http://repo.parabolagnulinux.org/\$repo/os/\$arch + +EOF + +rm .arch-chroot + +tar cjf $outdir/parabola-mips64el-$(LC_ALL=C date -u +%Y%m%d).tar.bz2 . diff --git a/src/mips64el-tools/mips-add b/src/mips64el-tools/mips-add new file mode 100755 index 0000000..402036e --- /dev/null +++ b/src/mips64el-tools/mips-add @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +. libremessages +if ! grep mips64el PKGBUILD >/dev/null; then # Add mips64el in ${arch} array if it isn't 'any' + warning "Adding mips64el arch" + sed -i "s/^\(arch=([^)anym]\+\))/\1 'mips64el')/" "PKGBUILD" + git add PKGBUILD + git commit +fi diff --git a/src/mips64el-tools/mipsrelease b/src/mips64el-tools/mipsrelease new file mode 100755 index 0000000..a8c2db6 --- /dev/null +++ b/src/mips64el-tools/mipsrelease @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Lic: GPLv3+ +# Author: Nicolas Reynolds <fauno@kiwwwi.com.ar> +# Local release of mips64el packages + clean ABS sync +# Called by HOOKLOCALRELEASE + +# $1 repo +# $2+ packages + +. libremessages +. $(librelib conf.sh) +load_files makepkg +check_vars makepkg CARCH PKGDEST PKGEXT || exit 1 +load_files libretools +check_vars libretools WORKDIR || exit 1 + +usage() { + echo "$0 repo" + echo + echo " release packages locally on \${PKGDEST}/stage3." + echo " and make a clean ABS sync " +} + +main() { + if [[ $# != 1 ]]; then + usage + exit 1 + fi + + repo=$1 + + if [ -z "${repo}" ]; then + error "Empty repo" + exit 1 + fi + + # Get all needed sources + load_PKGBUILD + pkgs=() + makepkg --source -f --skippgpcheck + + msg "Adding packages to [stage3]..." + for name in "${pkgname[@]}"; do + msg2 "${name} $(get_full_version $name)" + pkgs+=(${PKGDEST}/${name}-$(get_full_version $name)-*.pkg.tar.*) + done + + repo-add ${PKGDEST}/stage3.db.tar.gz "${pkgs[@]}" + + librestage ${repo} + + mkdir -p ${WORKDIR}/abs/${CARCH}/${repo} >/dev/null + + pushd ${WORKDIR}/abs/${CARCH}/${repo} >/dev/null + tar xvf $SRCPKGDEST/${pkgbase:-${pkgname[0]}}-$(get_full_version)${SRCEXT} + popd >/dev/null + + exit $? +} + +main "$@" diff --git a/src/pkgbuild-check-licenses b/src/pkgbuild-check-licenses new file mode 100755 index 0000000..85ca2c3 --- /dev/null +++ b/src/pkgbuild-check-licenses @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# pkgbuild-check-licenses + +# Copyright 2010 Haase Hernández +# Copyright 2010 Joseph Graham +# Copyright 2010 Joshua Ismael +# Copyright 2010 Nicolás Reynolds +# Copyright 2012-2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. $(librelib conf) + +# Usage: check_deps $pkgbuild +# Check whether a PKGBUILD package depends on non-free packages +check_licenses() ( + # Note that we use () instead of {} for this function; so that variables + # from the PKBUILD don't bubble up + local pkgbuild=$1 + load_PKGBUILD "$pkgbuild" + if [[ -z $pkgname ]]; then + return $_E_ERROR # not a PKGBUILD + fi + if [[ -z "${license[*]}" ]]; then + error "license array of %s %s is not set" "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + return $_E_ERROR + fi + + msg2 "Looking at license array of %s %s" "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + + local ret=$_E_OK + for _license in "${license[@]}"; do + if [[ ! -e "/usr/share/licenses/common/$_license" ]]; then + local s=$_E_OK + case "${_license#custom:}" in + WTFPL) + # accept as common, I think it should be in the licenses package + :;; + BSD1|BSD2|BSD3|MIT|X11) + # accept these as common; they can't be included in the + # 'licenses' package because some text must be customized + :;; + BSD4) + warning "The 4-clause BSD license is free but has practical problems.";; + BSD) + warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." + s=$_E_UNCOMMON + ;; + JSON) + error "License '%s' is a known non-free license." "$_license" + s=$_E_NONFREE + ;; + *) + warning "License '%s' is not a common license." "$_license" + s=$_E_UNCOMMON + ;; + esac + ret=$(($ret|$s)) + fi + done + return $ret +) + +usage() { + print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" + echo + prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' + echo + print "Exit status (add them for combinations):" + print " 0: Everything OK, no freedom issues" + print " 1: Ran with error" + print " 2: Uses uncommon licenses, check them" + print " 4: Uses known unacceptable licenses" + echo + print "Options:" + flag '-f' 'Allow running as root user' + flag '-h' 'Show this message' +} +_E_OK=0 +_E_ERROR=1 +_E_UNCOMMON=2 +_E_NONFREE=4 + +main() { + local asroot=false + while getopts 'fh' arg; do + case "$arg" in + f) asroot=true;; + h) usage; return $_E_OK;; + *) usage; return $_E_ERROR;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + pkgbuilds=("`pwd`/PKGBUILD") + else + pkgbuilds=("$@") + fi + + if [[ -w / ]] && ! $asroot; then + error "Run as normal user, or use the -f option to run as root." + return 1 + fi + + local ret=0 + for pkgbuild in "${pkgbuilds[@]}"; do + check_licenses "$pkgbuild" || ret=$(($ret|$?)) + done + return $ret +} + +main "$@" diff --git a/src/pkgbuild-check-nonfree b/src/pkgbuild-check-nonfree new file mode 100755 index 0000000..1cc0d9b --- /dev/null +++ b/src/pkgbuild-check-nonfree @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# pkgbuild-check-nonfree + +# Copyright 2010 Haase Hernández +# Copyright 2010 Joseph Graham +# Copyright 2010 Joshua Ismael +# Copyright 2012-2013 Luke Shumaker +# +# This file is part of Parabola. +# +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. libreblacklist +. $(librelib conf) + +# Usage: check_deps $pkgbuild +# Check whether a PKGBUILD package depends on non-free packages +check_deps() ( + # Note that we use () instead of {} for this function; so that variables + # from the PKBUILD don't bubble up + local pkgbuild=$1 + load_PKGBUILD "$pkgbuild" + if [[ -z "$pkgname" ]]; then + exit 1 # not a PKGBUILD + fi + + msg2 'Looking for unfree dependencies of %s %s' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + + local pkgs=( + # packages being built + "${pkgname[@]}" + # depends + "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" "${optdepends[@]%%:*}" + # mksource depends + "${mkdepends[@]}" + ) + local ret=0 + for pkg in "${pkgs[@]}"; do + local line="$(blacklist-cat|blacklist-lookup "$pkg")" + local rep="$(blacklist-get-rep <<<"$line")" + if [[ -z $line ]]; then + # not mentioned in blacklist; free + plain '%s: not blacklisted' "$pkg" + continue + elif [[ -z $rep ]]; then + # non-free with no replacement + plain '%s: blacklisted' "$pkg" + ret=1 + else + # non-free with free replacement + if [[ "$rep" == "$pkg" ]]; then + plain '%s: repackaged with the same name' "$pkg" + else + plain '%s: replaced by %s' "$pkg" "$rep" + fi + fi + done + return $ret +) + +usage() { + print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" + echo + prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' + echo + print "Exit status:" + print " 0: Everything OK, no freedom issues" + print " 1: Ran with error" + print " 15: Depends on non-free packages" + echo + print "Options:" + flag '-c' 'Use the cached blacklist, do not try downloading.' + flag '-f' 'Allow running as root user' + flag '-h' 'Show this message' +} + +main() { + local asroot=false + local cache=false + while getopts 'cfh' arg; do + case "$arg" in + c) cache=true;; + f) asroot=true;; + h) usage; return 0;; + *) usage; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + pkgbuilds=("`pwd`/PKGBUILD") + else + pkgbuilds=("$@") + fi + + if [[ -w / ]] && ! $asroot; then + error "Run as normal user, or use the -f option to run as root." + return 1 + fi + + $cache || blacklist-update || return 1 + + local ret=0 + for pkgbuild in "${pkgbuilds[@]}"; do + check_deps "$pkgbuild" || ret=15 + done + return $ret +} + +main "$@" diff --git a/src/toru/Makefile b/src/toru/Makefile new file mode 100644 index 0000000..2c76089 --- /dev/null +++ b/src/toru/Makefile @@ -0,0 +1 @@ +include ../../common.mk diff --git a/src/toru/toru b/src/toru/toru new file mode 100755 index 0000000..3c45efd --- /dev/null +++ b/src/toru/toru @@ -0,0 +1,301 @@ +#!/usr/bin/env bash +# Queries the ABS +# License: GPL3 + +## TODO +# * Add license text +# * Create symlinks from pkgbase to pkgname[@] for easy package finding + +## GOALS +# * Have a searchable database of PKGBUILD metadata +# * Have an interface for source-only builds +# * Possibility to hook up ABS dirs besides ABSROOT (low priority) +# * Tell updates and non available binary packages (working on this) + +source toru-utils + +# Saves contents on a named cache +# $1 cache name (repo) +# $2+ contents +function store_cache { + cache=$1; shift + + [ -z "$cache" ] && return 1 + + cat $@ > ${TORUPATH}/${cache}.cache + + return $? +} + +# Return cache contents +# $1 cache name +read_cache() { + cat ${TORUPATH}/${1}.cache 2>/dev/null + + return $? +} + +# Outputs an ordered package-fullpkgver array +print_package_array() { + echo "$@" | tr " " "\n" | sort -u +} + + +# Gets repo.db contents (unordered) +# $1 repo +get_db_contents() { + [ ! -r /var/lib/pacman/sync/$1.db ] && return 0 + + bsdtar -tf /var/lib/pacman/sync/$1.db | cut -d'/' -f1 | sort -u +} + +# Get the pkgname +# pkgname from pkgver separator can be either '-' or ' ' +extract_pkgname() { + echo "$@" | tr " " "\n" | sed "s/^\(.\+\)[- ][^-]\+-[^-]\+$/\1/" +} + +# Get all the pkgnames from a file +# pkgname from pkgver separator can be either '-' or ' ' +extract_pkgname_from_file() { + sed "s/^\(.\+\)[- ][^-]\+-[^-]\+$/\1/" $1 +} + +# Split pkgnames from pkgvers +split_pkgname_from_pkgver() { + sed "s/^\(.\+\)-\([^-]\+-[^-]\+\)$/\1 \2/" $1 +} + +# Get the fullpkgver +# pkgname from pkgver separator can be either '-' or ' ' +extract_fullpkgver() { + echo "$@" | tr " " "\n" | sed "s/^.\+[ -]\([^-]\+-[^-]\+\)$/\1/" +} + +# Checks if $1 is a valid repo +is_repo() { + if ! in_array ${1} ${REPOS[@]}; then + $quiet || warning "${1} is not a valid repo (check REPOS array at libretools.conf)" + return 1 + fi +} + +# Updates the database by finding all PKGBUILDS +# Workflow: +# * Find all PKGBUILDs on the ABS repo specified +# * Get all packages already on package repos +# * Compare them +# Args: +update() { + local update_sync_file=false +# The PKGBUILDs found + local -a pkgbuilds=() +# The list of pkgname-fullpkgver + local -a packages_in_abs=() + local -a pkg_updates=() + local -a package_paths=() + +# Traverse all specified repos + for __repo in $@; do +# Check if the repo is set as such, otherwise skip + is_repo ${__repo} || continue + +# Fullpath of the repo + _repopath=$(readlink -f ${__repo}) + +# This is the syncfile, stores the last date as content and mtime + local lastsyncfile=${TORUPATH}/${__repo}.lastsync + +# Find all the PKGBUILDs newer than the last update +# Update newer, otherwise everything + if [[ $force = true || ! -e ${lastsyncfile} ]]; then + + $quiet || warning "Forcing upgrade" +# Get all PKGBUILDs + pkgbuilds=($(find ${_repopath} -maxdepth 2 -type f -name 'PKGBUILD')) + + else + +# Only find newer than lastsyncfile and read everything else from cache + pkgbuilds=($(find ${_repopath} -maxdepth 2 -type f -name 'PKGBUILD' -newer ${lastsyncfile})) + packages_in_abs=($(read_cache ${__repo})) + + $quiet || msg2 "Getting ${#packages_in_abs[@]} packages from cache" + + fi + + package_paths=($(read_cache ${__repo}.paths || true)) + +# Inform how many PKGBUILDS were found and quit immediately if none + $quiet || msg "Found $((${#pkgbuilds[*]}-1)) PKGBUILDs to update" + +# Traverse all found PKGBUILDs + for _pkgbuild in ${pkgbuilds[@]}; do +# Update the sync file because there are pkgbuilds to update + update_sync_file=true + +# Load PKGBUILD's metadata + source ${_pkgbuild} || continue + +# Guess pkgbase from PKGBUILD's basedir + _pkgpath=$(dirname "${_pkgbuild}") + _pkgbase=${pkgbase:-${pkgname[0]}} + +# We won't need this (all unsets are for memory efficiency) + unset build package url md5sums install pkgdesc backup options +# TODO fill a license list + unset license +# TODO create source tarballs? + unset mksource +# TODO solve dependency tree? + unset depends makedepends + + for _pkg in ${pkgname[@]}; do +# Keep removing unneeded stuff + unset package_${_pkg} >/dev/null 2>&1 || true +# Fill the list of packages to find + packages_in_abs+=($_pkg-$(get_full_version $_pkg)) + package_paths+=($_pkg:$_pkgpath) + done # end pkgnames + + unset pkgbase pkgname pkgver pkgrel source epoch + done # end pkgbuilds + +# Sync! (Only if there was an actual sync) + ${update_sync_file} && lastsync ${lastsyncfile} + + if [ "${lastsyncfile}" -nt "${TORUPATH}/${__repo}.paths.cache" ]; then + print_package_array "${package_paths[@]}" > $TMPDIR/paths + store_cache ${__repo}.paths $TMPDIR/paths + fi + +# If there isn't an update cache or it's older than the last update, we check + if [ "${lastsyncfile}" -nt "${TORUPATH}/${__repo}.updates.cache" ]; then + +# Get repo database contents + packages_in_sync=($(get_db_contents ${__repo})) +# Drops arrays into files + print_package_array "${packages_in_abs[@]}" > ${TMPDIR}/packages_in_abs + print_package_array "${packages_in_sync[@]}" > ${TMPDIR}/packages_in_sync + +# Work with files + unset packages_in_abs package_in_sync + +# Use a different separator for pkgnames and pkgvers +# so we can join them by pkgname (first field) + split_pkgname_from_pkgver ${TMPDIR}/packages_in_abs | sort -k1b,1 > ${TMPDIR}/in_abs + split_pkgname_from_pkgver ${TMPDIR}/packages_in_sync | sort -k1b,1 > ${TMPDIR}/in_sync + + $quiet || msg "These packages are available to update" +# Join both files by pkgname, the end result is: +# pkgname syncver absver + join ${TMPDIR}/in_sync ${TMPDIR}/in_abs | \ + while read need_line; do + _pkg=$(echo "${need_line}" | cut -d' ' -f1) + _syncver=$(echo "${need_line}" | cut -d' ' -f2) + _absver=$(echo "${need_line}" | cut -d' ' -f3) + +# If the versions differ we need an update +# TODO move this to update query + if [ "${_syncver}" != "${_absver}" ]; then + $quiet || msg2 "$_pkg update from $_syncver to $_absver" + $quiet && echo "$_pkg" + +# FIXME this works all right but it's unset once the while ends + #pkg_updates+=("$_pkg") + +# Fix for the above problem, but it access the file every time instead of +# puting all packages together once + echo $_pkg >> ${TMPDIR}/updates + + fi + done # end need_line + + unset _pkg _syncver _absver need_line + +# Save the cache + store_cache ${__repo} ${TMPDIR}/packages_in_abs + +# See above FIXME + # print_package_array "${updates[@]}" > ${TMPDIR}/updates + if [ -r ${TMPDIR}/updates ]; then + store_cache ${__repo}.updates ${TMPDIR}/updates + fi + + else + $quiet || msg "Reading updates from cache..." + read_cache ${__repo}.updates + fi + + done # end repos +} + +# Find all the packages that are missing from the repo dbs (aka not built) +missing() { + true +} + +## Finds a PKGBUILD on toru's path cache +## usage: where_is <pkgname> +# Look in all caches but pick the first one +where_is() { + local __repo + local _path + for __repo in ${REPOS[@]}; do + _path=$(grep "^${1}:" "${TORUPATH}/${__repo}.paths.cache" 2>/dev/null | + cut -d: -f2) + + [ -n "${_path}" ] && break + done + + [ -z "$_path" ] && return 1 + + echo ${_path} +} + +# TODO: clean usage instructions +function usage { + echo "" + echo "$0 [options] repo1 ... repon" + echo "" + echo "Make a db containing PKGBUILD metadata." + echo "" + echo "-h : this message" +# echo "-a : update all repos at once" + echo "-u : update repo information" + echo "-q : quiet" + echo "-f : rebuild the db even if it is updated" + echo "-p <pkgname>: return the path for pkgname" + echo "" + exit 1 +} + +## MAIN +commands=() +repos=() +quiet=false +force=false +while getopts 'haqfpum' arg; do + case $arg in + h) usage; exit 0 ;; +# TODO: Update all repos on $REPOS array +# a) update_all_repos ;; + q) quiet=true ;; + f) force=true ;; + u) commands+=(update);; + p) shift $(( OPTIND - 1 )) + where_is "$1" || exit 1;; + m) commands+=(missing);; + esac + + shift $(( OPTIND - 1 )) +done + + +TMPDIR=$(mktemp -d) + +[[ -z ${TMPDIR} ]] && exit 1 + +${commands[0]} ${@} + +exit $? diff --git a/src/toru/toru-info b/src/toru/toru-info new file mode 100755 index 0000000..d73d2ad --- /dev/null +++ b/src/toru/toru-info @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Prints info about a given pkgname +. libremessages +. $(librelib conf) + +for _pkg in $@; do + _pkgbuild="$(toru-where $_pkg)" + + if [ -f "$_pkgbuild/PKGBUILD" ]; then + load_PKGBUILD "$_pkgbuild/PKGBUILD" 2>/dev/null || { + warning "Errors on %s" $_pkg + continue + } + + deps="${depends[@]} ${makedepends[@]} ${checkdepends[@]}" + repo="$(basename $(dirname "$_pkgbuild"))" + + msg "%s/%s %s-%s" $repo $_pkg $pkgver $pkgrel + msg2 "$pkgdesc" + msg2 "$url" + msg2 "Depends: ${deps}" + else + warning "%s doesn't exist" $_pkg + fi +done diff --git a/src/toru/toru-path b/src/toru/toru-path new file mode 100755 index 0000000..6c86e88 --- /dev/null +++ b/src/toru/toru-path @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +source toru-utils + +TORUPATH=${T:-${TORUPATH}} +VERBOSE=${V:-false} + +if [ ! -w "$TORUPATH" ]; then + error "Toru's path isn't writable. Please check $TORUPATH" + exit 1 +fi + +LASTSYNCFILE=${TORUPATH}/lastsync.paths +PATHFILE=${TORUPATH}/paths.tch + +if [ ! -e "${PATHFILE}" ]; then + tcamgr create "${PATHFILE}" +fi + +# TODO pass other paths via flags +# ABSROOT has trailing slash +fullrepos=() +for (( i = ${#REPOS[@]}-1 ; i >= 0 ; i-- )); do + ${VERBOSE} && msg "Processing [%s]" ${REPOS[$i]} + + [ -d "${ABSROOT}${REPOS[$i]}" ] && \ + fullrepos+=("${ABSROOT}${REPOS[$i]}") +done +pkgbuilds=($(get_pkgbuilds ${fullrepos[@]})) + +msg "Updating path cache" +msg2 "${#pkgbuilds[@]} PKGBUILDs to update" +for _pkgbuild in ${pkgbuilds[@]}; do +# plain "$_pkgbuild" + load_PKGBUILD "${_pkgbuild}" >/dev/null 2>&1 || { + error "${_pkgbuild} contains errors, skipping" + continue + } + + fullpath=$(dirname ${_pkgbuild}) + + for _pkg in ${pkgbase} ${pkgname[@]} ${provides[@]}; do + $VERBOSE && msg2 "${_pkg} -> ${fullpath}" + tcamgr put ${PATHFILE} ${_pkg/[<>=]*} ${fullpath} + done +done + +lastsync ${LASTSYNCFILE} diff --git a/src/toru/toru-utils b/src/toru/toru-utils new file mode 100755 index 0000000..96aa35e --- /dev/null +++ b/src/toru/toru-utils @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +. libremessages +. $(librelib conf.sh) +load_files libretools +check_vars libretools TORUPATH || exit 1 + +LASTSYNCFILE=${TORUPATH}/lastsync +FORCE=false +QUIET=false +DEBUG=false + +# Stores the lastsync date +lastsync() { + local lastsyncfile + + lastsyncfile=$1 + + [ -e ${lastsyncfile} -a ! -w ${lastsyncfile} ] && { + error "The sync date can't be saved. ${lastsyncfile} isn't writable." + return 1 + } + + date +%s > ${lastsyncfile} + touch ${lastsyncfile} +} + +get_dbs() { + local _db + for _db in /var/lib/pacman/sync/*.db; do + bsdtar tf ${_db} | cut -d'/' -f1 | sort -u + done +} + +# repo paths +get_pkgbuilds() { + pkgbuilds=() + + if [[ $FORCE = true || ! -e ${LASTSYNCFILE} ]]; then + + $QUIET || warning "Forcing upgrade" +# Get all PKGBUILDs + extra="" + else +# Only find newer than lastsyncfile and read everything else from cache + extra=" -newer ${LASTSYNCFILE}" + fi + +# Return all PKGBUILDs found + find $@ -mindepth 2 -maxdepth 3 -type f -name 'PKGBUILD' ${extra} +} + +# End inmediately but print a useful message +trap_exit() { + error "$@" + + exit 1 +} + +# Trap signals from makepkg +set -E +trap 'trap_exit "TERM signal caught. Exiting..."' TERM HUP QUIT +trap 'trap_exit "Aborted by user! Exiting..."' INT +trap 'trap_exit "An unknown error has occurred. Exiting..."' ERR diff --git a/src/toru/toru-where b/src/toru/toru-where new file mode 100755 index 0000000..4b3ff1b --- /dev/null +++ b/src/toru/toru-where @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Locates a PKGBUILD dir on toru's path cache +. $(librelib conf.sh) +load_files libretools +check_vars libretools TORUPATH || exit 1 + +PATHFILE=${TORUPATH}/paths.tch + +tcamgr get ${PATHFILE} $1 2>/dev/null || echo "" diff --git a/src/treepkg b/src/treepkg new file mode 100755 index 0000000..8219ce6 --- /dev/null +++ b/src/treepkg @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +#set -x +# (c) 2012 Nicolás Reynolds <fauno@parabola.nu> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +source libremessages +source $(librelib conf.sh) + +load_files libretools +check_vars libretools HOOKPREBUILD FULLBUILDCMD HOOKLOCALRELEASE +load_files makepkg + +term_title "$(basename $0)" + +# End inmediately but print an useful message +trap_exit() { + term_title "error!" + error "($(basename $0)) $@ (leftovers on ${BUILDDIR})" + exit 1 +} + +# Trap signals from makepkg +set -E +trap 'trap_exit "TERM signal caught. Exiting..."' TERM HUP QUIT +trap 'trap_exit "Aborted by user! Exiting..."' INT +trap 'trap_exit "An unknown error has occurred. Exiting..."' ERR + +# Add line to build order cache in CSV format +# *must* be run from the PKGBUILD path +# status;depth;pkgbase;[epoch:]pkgver-pkgrel;path;repo +# $1 status +# $2 pkgname +add_order() { + echo "${1};${DEPTH};${2:-${pkgbase}};$(get_full_version ${2});${PWD};$(guess_repo "$PWD")" >> "${BUILDORDER}" + ${VERBOSE} && msg2 "%${DEPTH}s${2:-${pkgbase}} [${1}]" || true +} + +# Bury a package deeper in the tree +# $1 pkgbase +# $2 nextdepth +bury() { + # Bury only if we are going to build the dep + # Get it's current depth and dir name + local current_depth=$(grep "build;[0-9]\+;${1};" "${BUILDORDER}" | cut -d ';' -f 2) + local current_name="$(printf "%03d" ${current_depth})_${1}" + + # If there's a depth or the package is not the root of the build tree (which + # can lead to funny chicken-and-egg problems), update the depth to the current + # package next-depth and rename the dir too + if [ -z "${current_depth}" ]; then return; fi + if [ -z "${current_name}" ]; then return; fi + if [ ${current_depth} -eq 0 ]; then return; fi + if [ ${current_depth} -ge $2 ]; then return; fi + + ${VERBOSE} && msg "Burying ${1} from ${current_depth} to ${2}" + + { + sed -i "s|^\(build;\)\([0-9]\+\)\(;${1};.*\)$|\1${2}\3|" "${BUILDORDER}" && \ + mv "${BUILDDIR}/${current_name}" "${BUILDDIR}/$(printf "%03d" ${2})_${1}" + } || return 1 +} + +# Guess the repo from the pkgbase path +# $1 path, pwd or toru-where +guess_repo() { + basename "$(dirname "${1}")" +} + +if [ ! -f PKGBUILD ]; then + error "Missing PKGBUILD ($PWD)" + exit 1 +fi + +if ! load_PKGBUILD ; then + error "Can't source PKGBUILD" + exit 1 +fi + +# Save resources +unset pkgdesc arch license groups backup install md5sums sha1sums \ + sha256sums source options >/dev/null 2>&1 + +unset build package >/dev/null 2>&1 + +for _pkg in "${pkgname[@]}"; do + unset package_${_pkg} >/dev/null 2>&1 || true +done +## + +# Get useful values +pkgbase="${pkgbase:-${pkgname[0]}}" + +# Get or set the work dir +BUILDDIR="${1:-$(mktemp -d /tmp/${pkgbase}-treepkg-XXXx)}" +BUILDORDER="${BUILDDIR}/BUILDORDER" +DEPTH=${2:-0} +NEXTDEPTH=$((${DEPTH} + 1)) +# This can be set as env vars (ie: $ V=false B=false treepkg) +# TODO Turn into flags? +VERBOSE=${V:-true} +BUILD=${B:-true} +CLEANUP=${C:-true} +# Skip BUILDORDER creation and build anything on BUILDDIR +BUILDNOW=${N:-false} + +if [[ ! -z $1 ]] && [[ $DEPTH -eq 0 ]]; then + BUILDNOW=true +fi + +if ! ${BUILDNOW}; then + # ensure it exists + touch "${BUILDORDER}" + + # If this package is already built quit silently + for _pkg in "${pkgname[@]}"; do + if is_built "${_pkg}" "$(get_full_version ${_pkg})"; then + add_order "ignore" + exit 0 + fi + done + + # Ignore if already in build order + egrep -q ";${pkgbase};" "${BUILDORDER}" && exit 0 + + # Add pkgbase to build order + add_order "build" + + # Copy the directory to the build dir + # TODO run makepkg --source to avoid moving garbage around? + cp -r "${PWD}" "${BUILDDIR}/$(printf "%03d" ${DEPTH})_${pkgbase}" + + # Cleanup dep versioning + deps=($(echo "${depends[@]} ${makedepends[@]}" | \ + sed "s/[=<>]\+[^ ]\+//g" | \ + tr ' ' "\n" | \ + sort -u)) + + # NOTE: getting depends from package() is a PITA + for _dep in "${deps[@]}"; do + # Move deps deeper in the tree if + # pkgbase - dep1 + # \ dep2 - dep1 + # dep1 should be depth + 1 + egrep -q ";${_dep};" "${BUILDORDER}" && bury "${_dep}" ${NEXTDEPTH} + + # Ask toru where's a PKGBUILD + depdir="$(toru-where ${_dep})" + + if [[ -z ${depdir} ]] || [[ ! -d ${depdir} ]]; then + # We specify the pkgname because we can't source the dep PKGBUILD + # Normally 'any' packages are missing from our work ABS + add_order "missing" "${_dep}" + continue + fi + + pushd "${depdir}" >/dev/null + + # Run itself over dependencies + $0 "${BUILDDIR}" ${NEXTDEPTH} + + done + # End BUILD now +fi + +# Only build at the end +if [ ${DEPTH} -eq 0 ]; then + ${VERBOSE} && msg "Starting build" || true + + if ${BUILD}; then + ${VERBOSE} && msg "Build tree stored in ${BUILDORDER}" || true + + # Build everything sorting the build dir + # The reverse order ensures we start by the deepest packages + for _pkg in $(ls -r "${BUILDDIR}"); do + # Ignore if there's no PKGBUILD + if [ ! -f "${BUILDDIR}/${_pkg}/PKGBUILD" ]; then continue; fi + # Skip if already built (faster than calling is_build again) + if [ -f "${BUILDDIR}/${_pkg}/built_ok" ]; then continue; fi + + ${VERBOSE} && msg "Building ${_pkg/_/ }" || true + + # Remove leading zeros and space if any + term_title "$(echo ${_pkg/_/ } | sed "s/^0\+ \?//")" + + # Run build command + pushd "${BUILDDIR}/${_pkg}" >/dev/null + sudo -E pacman -Syu --noconfirm + + ${HOOKPREBUILD} + + ${FULLBUILDCMD} + # Run local release hook with $1 = $repo + ${HOOKLOCALRELEASE} $(egrep ";${_pkg#*_};" "${BUILDORDER}" | cut -d';' -f6) + + touch built_ok + popd >/dev/null + done + + else + # Just print the working dir + ${VERBOSE} || echo "${BUILDORDER}" || true + fi + + if ${CLEANUP} ; then + msg2 "Removing ${BUILDDIR}" + rm -rf "${BUILDDIR}" + fi + +fi + +term_title "done" +exit $? |