diff options
Diffstat (limited to 'src')
89 files changed, 7918 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e1e5cf4 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,7 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir) + +srcfiles-ignore/$(srcdir) = $(addsuffix /%,abslibre-tools chroot-tools devtools gitget lib librefetch toru xbs xbs-abs xbs-abslibre) + +include $(topsrcdir)/automake.tail.mk diff --git a/src/abslibre-tools/Makefile b/src/abslibre-tools/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/abslibre-tools/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/abslibre-tools/createworkdir b/src/abslibre-tools/createworkdir new file mode 100755 index 0000000..5599de9 --- /dev/null +++ b/src/abslibre-tools/createworkdir @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# CreateWorkDir +# Creates a dir structure for working with Parabola packages + +# Copyright (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +set -euE + +. libremessages +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools WORKDIR ABSLIBRERECV ABSLIBRESEND # these are asked for by `xbs download` + +trap 'error "Aborting..."' EXIT + +msg "Creating WORKDIR at %s..." "$WORKDIR" +mkdir -p "$WORKDIR" + +msg "Creating staging directory in WORKDIR..." +mkdir -p "$WORKDIR/staging" + +cmd=(xbs -b abslibre download) +if ! "${cmd[@]}"; then + error "Could not clone ABSLibre" + plain "Try running this command:" + echo + printf '%q ' "${cmd[@]}" + echo + exit 1 +fi + +msg "Finished, your packaging directory tree looks like this now:" +ls --color=auto "${WORKDIR}"/* + +trap -- EXIT diff --git a/src/abslibre-tools/createworkdir.md b/src/abslibre-tools/createworkdir.md new file mode 100644 index 0000000..e50b00f --- /dev/null +++ b/src/abslibre-tools/createworkdir.md @@ -0,0 +1,36 @@ +# CreateWorkDir + +This script recreates a proper directory tree for packaging. Its aim is to help +you be organized with the work you do as a packager, and establish a certain +standard for packages' publication, so you don't have to lose much time with +them. Just package and upload! + +It will create a directory tree like this: + + $WORKDIR/ + ├── abslibre/ + │ ├── .git/ + │ ├── libre/<PKGBUILDS> + │ └── libre-testing/<PKGBUILDS> + └── staging/ + ├── libre/ + └── libre-testing/ + +*Related Variables* + - WORKDIR + +## staging/ + +This directory contains one directory for each repository, where the resulting +packages are in moved for syncing against the main repository on Parabola's +server. This directory is architecture independent. + +## abslibre/ + +This is the git repo for Parabola's PKGBUILDs. Here you can find the ABS tree +for our packages, and also where you'll have to put new ones for commit. + +(You'll need push access to Parabola's main server, but pulling is public.) + +*Related Variables* + - ABSLIBREGIT diff --git a/src/abslibre-tools/diff-unfree b/src/abslibre-tools/diff-unfree new file mode 100755 index 0000000..12c919f --- /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 (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# +# License: GNU GPLv3+ +# +# 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 DIFFPROG || exit 1 + +usage() { + print "Usage: %s [community|packages] [unfree-package] [repo]" "${0##*/}" + print "Usage: %s --help" "${0##*/}" + prose "Helps you diff build scripts from ABSLibre against (Unfree) ABS." + echo + prose "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 %s..." "$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 "%s" "$(basename "${_file}")" + ${DIFFPROG} "$PWD/$(basename "${_file}")" "${_file}" + done +} + +main "$@" diff --git a/src/abslibre-tools/libreaddiff b/src/abslibre-tools/libreaddiff new file mode 100755 index 0000000..8698920 --- /dev/null +++ b/src/abslibre-tools/libreaddiff @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012 Daniel Molina (lluvia) +# +# License: GNU GPLv3+ +# +# 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 + +. libremessages +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools WORKDIR + +for arg in "$@" ; do + case "$arg" in + -h|--h|--he|--hel|--help|-\?) + { + print 'Usage: %s repo [arch]' "${0##*/}" + echo + prose "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" ]] || [[ "$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/libredbdiff b/src/abslibre-tools/libredbdiff new file mode 100755 index 0000000..09c7ad1 --- /dev/null +++ b/src/abslibre-tools/libredbdiff @@ -0,0 +1,360 @@ +#!/usr/bin/env bash +name="Libredbdiff" + +# Copyright (C) 2014 Esteban Carnevale <alfplayer@mailoo.org> +# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +baseconfpath="/etc/libredbdiff" +basedbpath="/var/lib/libredbdiff" + +conffile="$baseconfpath/pacman.conf.parabola" +conffilearch="$baseconfpath/pacman.conf.archlinux" + +dbpath="$basedbpath/pacman.parabola" +dbpatharch="$basedbpath/pacman.archlinux" + +mirrorlist="$baseconfpath/mirrorlist.parabola" +mirrorlistarch="$baseconfpath/mirrorlist.archlinux" + +mirror='http://repo.parabola.nu/$repo/os/$arch' +mirrorarch='http://mirrors.kernel.org/archlinux/$repo/os/$arch' + +repos="libre pcr libre-multilib nonprism" + +field_pkgname_parabola=30 +field_pkgname_arch=30 + + +. libremessages + +cmd="${0##*/}" + +arch_packages_tmp="/tmp/${cmd}.arch-packages" +parabola_packages_tmp="/tmp/${cmd}.parabola-packages" + +field_pkgname_total="$((${field_pkgname_parabola} + ${field_pkgname_arch}))" +printf_format="%s %-${field_pkgname_parabola}s%-${field_pkgname_arch}s %s | %s\n" +printf_format_noarch="%s %-${field_pkgname_total}s %s\n" + +downloadfile() { + local outfile=$1 + local url=$2 + local mesg=("${@:3}") + if [[ ! -e $outfile ]] ; then + msg "${mesg[@]}" + if wget -q "$url" -O "$outfile"; then + return 255 + else + die "Failed to download %q. Exiting." "$outfile" + fi + elif [[ $init ]]; then + warning "%q already exists. Skipping." "$outfile" + fi +} + +enablerepo() { + repo="$1" + conffile_arg="$2" + msg2 "Enabling repo %q in %q" "$repo" "${conffile_arg}" + sed -i "s/\#\[$repo\]/[$repo]/" "${conffile_arg}" + sed -i "\/\[$repo\]/,+1 s/#Include/Include/" "${conffile_arg}" +} + +createdir() { + local dir=$1 + if [[ ! -e $dir ]] ; then + msg "Creating directory %q" "$dir" + mkdir -- "$1" || die "Failed to create directory %q. Exiting." "$dir" + elif [[ $init ]]; then + warning "%q already exists. Skipping." "$dir" + fi +} + +setmirror() { + local distro="$1" + local mirror="$2" + local mirrorlist="$3" + if [[ $init ]] && [[ $mirror ]]; then + mirrorescaped="${mirror//./\\.}" + mirrorescaped="${mirrorescaped//\$/\\$}" + msg2 "Setting %s as the only enabled %s mirror." "${mirror}" "${distro}" + sed -i 's|^#\(Server = '"${mirrorescaped}"'\)$|\1|' "${mirrorlist}" + fi +} + +filenotfound() { + local file=$1 + if [[ ! -r $1 ]]; then + error "Could not read %q." "$file" + die "Nothing done. It may be necessary to run %q without \ +arguments as root to initialize %s." "$cmd" "$name" + fi +} + +initialize() { + createdir "$baseconfpath" + createdir "$basedbpath" + + downloadfile \ + "${conffile}" \ + "https://projects.parabola.nu/abslibre.git/plain/libre/pacman/pacman.conf.x86_64" \ + "Downloading Parabola %q" \ + pacman.conf + if [[ $? == 255 ]] ; then + msg2 "Setting DBPath in %q" "${conffile}" + sed -i "s|^#DBPath .*|DBPath = ${dbpath}|" "${conffile}" + enablerepo nonprism "${conffile}" + enablerepo pcr "${conffile}" + enablerepo libre-multilib "${conffile}" + enablerepo multilib "${conffile}" + fi + + downloadfile \ + "${conffilearch}" \ + "https://projects.archlinux.org/svntogit/packages.git/plain/pacman/trunk/pacman.conf.x86_64" \ + "Downloading Arch %q" \ + pacman.conf + if [[ $? == 255 ]] ; then + msg2 "Setting DBPath in %q" "${conffilearch}" + sed -i "s|^#DBPath .*|DBPath = ${dbpatharch}|" "${conffilearch}" + msg2 "Setting Arch mirrorlist file in %q" "${conffilearch}" + sed -i "s|/etc/pacman\.d/mirrorlist$|$baseconfpath/mirrorlist.archlinux|" \ + "${conffilearch}" + enablerepo multilib "${conffilearch}" + fi + + downloadfile \ + "${mirrorlist}" \ + "https://repo.parabola.nu/mirrorlist.txt" \ + "Downloading Parabola %q" \ + mirrorlist + if [[ $? == 255 ]] ; then + sed -i 's|^Server|#Server|' "${mirrorlist}" + setmirror "Parabola" "$mirror" "$mirrorlist" + fi + + downloadfile \ + "${mirrorlistarch}" \ + "https://projects.archlinux.org/svntogit/packages.git/plain/pacman-mirrorlist/trunk/mirrorlist" \ + "Downloading Arch %q" \ + mirrorlist + if [[ $? == 255 ]] ; then + setmirror "Arch" "$mirrorarch" "$mirrorlistarch" + fi +} + +repo_test() { + for repo in ${repos} ; do + if [[ $repo == $1 ]] ; then + found=1 + return 0 + fi + done + if [[ $found != 1 ]] ; then + die "The specified Parabola repo \"%s\" cannot be compared. It's not in the list of repos in the configuration variable \"repos\"." "$1" + fi +} + +compare_pkgs() { + local cmp + if [[ ${verarch[$pkgname]} ]] ; then + cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$pkgname]}") + if [[ $cmp -lt 0 ]]; then + printf "${printf_format}" \ + '=' \ + "${pkgname}" \ + "" \ + "${ver[$pkgname]}" \ + "${verarch[$pkgname]}" + fi + elif [[ ${provides[$pkgname]} ]]; then + for provide in "${provides[$pkgname]}"; do + if [[ ${verarch["$provide"]} ]]; then + cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$provide]}") + if [[ $cmp -lt 0 ]]; then + printf "${printf_format}" \ + 'p' \ + "${pkgname}" \ + "${provide}" \ + "${ver[$pkgname]}" \ + "${verarch[$provide]}" + fi + fi + done + else + printf "${printf_format_noarch}" \ + 'o' \ + "${pkgname}" \ + "${ver[$pkgname]}" + fi +} + +print_cmp() { + local repo="$1" + awk -F/ -v repo="$repo" \ + '$1 == repo {print $2}' \ + ${parabola_packages_tmp} | \ + while read -a line ; do + ver["${line[0]}"]="${line[1]}" + provides[${line[0]}]="${line[@]:2}" + pkgname=${line[0]} + compare_pkgs + done +} + +usage() { + print "Usage: %q [-n|-h]" "$cmd" + print 'Show packages that need to be updated from Arch repositories.' + echo + prose "Compares packages in Parabola repositories. Packages from + all configured Parabola repositories are compared. A Parabola + repository name can be specified as argument to compare only + packages in that repository." + echo + prose "The default mode of operation is to download/update all necessary + files for comparison, but not compare them. Specify the \`-n\` + flag to not download anything, but to compare already downloaded + files." + echo + print 'Options:' + flag '-n' "Don't update anything, just compare already downloaded files." + flag '-h' 'Show this message' + echo + print "Output format:" + print "type_character parabola_pkgname (arch_pkgname) parabola_pkgver - (arch_pkgver)" + echo + print "Type characters:" + flag '=' "Arch package with the same pkgname and greater pkgver was found" + flag 'p' "Arch package with pkgname equal to a provide and greater + pkgver was found In this case arch_pkgname is a provide of + parabola_pkgname" + flag 'o' "No Arch package with the same pkgname or with pkgname equal to + a provide was found" +} + +main() { + local UPDATE=1 + local arg + local repo_arg + + for arg in "$@"; do + case "$arg" in + -n|--noupdate) + UPDATE=0 + ;; + -h|--help) + usage + return 0 + ;; + *) + repo_test "$arg" + repo_arg="$arg" + break + ;; + esac + done + + if (( $UPDATE )) ; then + if [[ $EUID != 0 ]]; then + die "To initialize %s or update %s pacman databases, %s must be run as root (without arguments). Nothing done." \ + "$name" \ + "$name" \ + "$cmd" + fi + + if ! [[ -e "${conffile}" && \ + -e "${conffilearch}" && \ + -e "${mirrorlist}" && \ + -e "${mirrorlist}" ]]; then + warning "At least one %s configuration file is missing." \ + "${name}" + msg "Downloading and preparing missing configuration files." + init=1 + fi + + createdir "$baseconfpath" + createdir "$basedbpath" + + initialize + + if ! [[ -d "$dbpath" && \ + -d "$dbpatharch" ]]; then + warning "At least one %s pacman DB directory is missing. Synchronizing %s DB files." \ + "${name}" "${name}" + fi + + createdir "$dbpath" + msg "Synchronizing %s pacman databases for Parabola" "$name" + pacman --config "${conffile}" -Sy || + die "Failed to synchronize pacman database for Parabola. Exiting." + + createdir "$dbpatharch" + msg "Synchronizing %s pacman databases for Arch" "$name" + pacman --config "${conffilearch}" -b "${dbpatharch}" -Sy || + die "Failed to synchronize pacman database for Arch. Exiting." + + msg "%s pacman databases are updated. %s is ready. Run %q -n to print results." \ + "$name" "$name" "$cmd" + return 0 + else + filenotfound "${dbpath}" + filenotfound "${dbpatharch}" + filenotfound "${conffile}" + filenotfound "${conffilearch}" + filenotfound "${mirrorlist}" + filenotfound "${mirrorlistarch}" + + unset provides ver verarch + declare -gA provides ver verarch + + if ! [[ -d "$dbpath" && \ + -d "$dbpatharch" ]]; then + die "At least one %s pacman DB directory is missing. To update %s pacman databases, %s must be run as root. Nothing done." \ + "$name" \ + "$name" \ + "$cmd" + fi + + pacman --config "${conffilearch}" -Ss | \ + grep -v '^ ' | \ + awk -F/ '{print $2}' \ + > ${arch_packages_tmp} || \ + die "pacman command to get Arch package data has failed. Exiting." + chmod 777 ${arch_packages_tmp} + + while read -a line; do + verarch["${line[0]}"]="${line[1]}" + done < ${arch_packages_tmp} + + expac --config "${conffile}" -S '%r/%n %v %S' \ + > ${parabola_packages_tmp} || \ + die "expac command to get Parabola package data has failed. Exiting." + chmod 777 ${parabola_packages_tmp} + + if [[ ${repo_arg} ]] ; then + print_cmp "${repo_arg}" + else + for repo in ${repos} ; do + echo "[$repo]" + print_cmp "$repo" + done + fi + fi +} + +main "$@" diff --git a/src/abslibre-tools/librerelease b/src/abslibre-tools/librerelease new file mode 100755 index 0000000..b7a77d3 --- /dev/null +++ b/src/abslibre-tools/librerelease @@ -0,0 +1,257 @@ +#!/usr/bin/env bash +# Librerelease +# Uploads packages and releases them + +# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2013 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2013 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the create_signature() function: +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# +# License: GNU GPLv3+ +# +# 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/>. + +# create_signature() is taken from pacman:makepkg, which is GPLv2+, +# so we take the '+' to combine it with our GPLv3+. + +. libremessages +. "$(librelib conf.sh)" + +dryrun="" +upload_only=false +readonly rsync_flags=( + --no-group + --no-perms + --copy-links + --hard-links + --partial + --human-readable + --progress + -e ssh +) + +# Functions #################################################################### + +list0_files() { + find -L "${WORKDIR}/staging" -type f -not -name '*.lock' \ + -exec realpath -z --relative-to="${WORKDIR}/staging" {} + +} + +# This function is taken almost verbatim from makepkg +create_signature() { + local ret=0 + local filename="$1" + msg "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" || ret=$? + + + if (( ! ret )); then + msg2 "Created signature file %s." "$filename.sig" + else + error "Failed to sign package file." + return $ret + fi +} + +sign_packages() { + IFS=$'\n' + local files=($(find "${WORKDIR}/staging/" \ + \( -type d -name "${ABSLIBREDEST##*/}" \) -prune \ + -o \( -type f -not -iname '*.sig' \) -print)) + local file + for file in "${files[@]}"; do + if [[ -f "${file}.sig" ]]; then + msg2 "File signature found, verifying..." + + # Verify that the signature is correct, else remove for re-signing + if ! gpg --quiet --verify "${file}.sig" >/dev/null 2>&1; then + error "Failed! Re-signing..." + rm -f "${file}.sig" + fi + fi + + if ! [[ -f "${file}.sig" ]]; then + create_signature "$file" || return 2 + fi + done +} + +# Clean everything if not in dry-run mode +clean_files() { + local file_list=$1 + + local rmcmd=(rm -fv) + if [[ -n "${dryrun}" ]]; then + rmcmd=(printf "$(_ "removed '%s' (dry-run)")\n") + fi + + msg "Removing files from local staging directory" + cd "${WORKDIR}/staging" && xargs -0r -a "$file_list" "${rmcmd[@]}" + cd "${WORKDIR}/staging" && find . -mindepth 1 -type d -empty \ + -exec rmdir -p {} + 2>/dev/null +} + +################################################################################ + +usage() { + print "Usage: %s [OPTIONS]" "${0##*/}" + echo + prose 'This script uploads packages on $WORKDIR/staging + to the Parabola server.' + echo + print "Options:" + flag '-c' 'Clean; delete packages in $WORKDIR/staging' + flag '-l' "List; list packages but not upload them" + flag '-u' "Upload-only; do not run db-update on the server" + + flag '-n' "Dry-run; don't actually do anything" + flag '-h' "Show this message" +} + +main() { + if [[ -w / ]]; then + error "This program should be run as regular user" + return 1 + fi + + # Parse options + local mode="release_packages" + while getopts 'clunh' arg; do + case $arg in + c) mode=clean ;; + l) mode=pretty_print_packages ;; + u) upload_only=true ;; + n) dryrun="--dry-run" ;; + h) mode=usage ;; + *) usage >&2; return 1 ;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# != 0 ]]; then + usage >&2 + 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 ABSLIBREDEST || return 1 + REPODEST+='/staging/' + # The following settings are actually optional + #check_vars libretools HOOKPRERELEASE HOOKPOSTRELEASE || return 1 + + "$mode" +} + +# The different modes (sans 'usage') ########################################### + +pretty_print_packages() { + find "$WORKDIR/staging/" -mindepth 1 -maxdepth 1 -type d -not -empty | sort | + while read -r path; do + msg2 "${path##*/}" + cd "$path" + find -L . -type f -not -name '*.lock' | sed 's|^\./| |' | sort + done +} + +clean() { + lock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" + + lock_close 8 + + clean_files "$file_list" +} + +release_packages() { + if [[ -n $HOOKPRERELEASE ]]; then + msg "Running HOOKPRERELEASE..." + plain '%s' "${HOOKPRERELEASE}" + bash -c "${HOOKPRERELEASE}" + fi + + lock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + + 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 {} + + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" + + lock_close 8 + + msg "%s to upload" "$(cd "${WORKDIR}/staging" && du -hc --files0-from="$file_list" | sed -n '$s/\t.*//p')" + msg "Uploading packages..." + xargs -0r -a "$file_list" dirname -z | ssh "${REPODEST%%:*}" "$(printf 'mkdir -p -- %q && cd %q && xargs -0r mkdir -pv --' "${REPODEST#*:}"{,})" + if ! rsync ${dryrun} "${rsync_flags[@]}" \ + -0 --files-from="$file_list" \ + "${WORKDIR}/staging" \ + "${REPODEST}/" + then + error "Sync failed, try again" + return 1 + fi + + clean_files "$file_list" + + if $upload_only; then + return 0 + fi + + msg "Running db-update on repos" + ssh "${REPODEST%%:*}" "$(printf 'STAGING=%q dbscripts/db-update' "${REPODEST#*:}")" + + if [[ -n $HOOKPOSTRELEASE ]]; then + msg "Running HOOKPOSTRELEASE..." + plain '%s' "${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..39523ac --- /dev/null +++ b/src/abslibre-tools/librestage @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# LibreStage +# Prepares packages for upload + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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)" + +usage() { + print "Usage: %s [REPO]" "${0##*/}" + print "Stages the package(s) build by ./PKGBUILD for upload." + echo + prose "The package(s) are staged for the named repository, or the name + of the parent directory if a repository is not named." +} + +main() { + if [[ -w / ]]; then + error "This program should be run as a regular user" + return 1 + fi + + # Parse options, set up + while getopts 'h' arg; do + case $arg in + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + local repo= + case $# in + 0) repo="$(basename "$(dirname "$PWD")")";; + 1) repo=$1;; + *) usage >&2; return 1;; + esac + + if ! [[ -e ./PKGBUILD ]]; then + error "PKGBUILD not found" + return 1 + fi + + if ! xbs -b abslibre status; then + error "There are uncommitted changes in the current directory" + return 1 + fi + + # Load configuration + load_files libretools + # ABSLIBREDEST is used by xbs release-client + check_vars libretools WORKDIR ARCHES ABSLIBREDEST || return 1 + load_files makepkg # for PKGDEST and SRCDEST, which are optional + load_files librefetch # for MIRRORS, which is optional + + # Load the PKGBUILD + load_PKGBUILD + + # Now for the main routine. + local staged=false + slock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for a shared lock on the staging directory' + + # Look for makepkg output + local CARCH _pkgname pkgfile + for CARCH in "${ARCHES[@]}" any; do + for _pkgname in "${pkgname[@]}" "${pkgname[@]/%/-debug}"; do + if ! pkgfile=$(find_cached_package "$_pkgname" "$(get_full_version "$_pkgname")" "$CARCH"); then + continue + fi + + msg 'Found package: %s' "${pkgfile##*/}" + + # This little check is from devtools:commitpkg + if grep -q "packager = Unknown Packager" <(bsdtar -xOqf "$pkgfile" .PKGINFO); then + die "PACKAGER wes not set when building package" + fi + + xbs -b abslibre release-client "$repo" "$CARCH" + mkdir -p "${WORKDIR}/staging/${repo}" + if cp "$pkgfile" "${WORKDIR}/staging/${repo}/${pkgfile##*/}"; 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 + + # Look for librefetch output + local netfile mirror path + local srcurl srcname srcpath + for netfile in "${source[@]}"; do + for mirror in "${MIRRORS[@]}"; do + srcurl=${netfile#*::} + if [[ "$srcurl" == "$mirror"* ]]; then + if [[ $netfile = *::* ]]; then + srcname=${netfile%%::*} + else + srcname=${netfile##*/} + fi + + srcpath='' + for path in "./$srcname" "${SRCDEST:-.}/$srcname"; do + if [[ -f "$path" ]]; then + srcpath="$path" + break + fi + done + if [[ -n "$srcpath" ]]; then + msg "Found generated source file: %s" "$srcname" + local dest="${WORKDIR}/staging/other/${srcurl##"$mirror"}" + mkdir -p -- "${dest%/*}" + if cp "$srcpath" "$dest"; then + msg2 "%s staged on [%s]" "$srcname" other + staged=true + else + error "Can't put %s on [%s]" "$srcname" other + return 1 + fi + fi + break + fi + done + done + + if $staged ; then + return 0 + else + error "Nothing was staged" + return 1 + fi +} + +main "$@" @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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 + +usage() { + print "Usage: %s [-h] PKGNAME [PKGNAME2 PKGNAME3...]" "${0##*/}" + print "Downloads packages from the AUR, and does basic freedom checks." + echo + prose "This script will download packages from AUR to the current + directory and check their license for nonfree issues. This does + not mean that they are free; they may be incorrectly labeled, or + have other freedom issues. It's a tool to help Parabola + 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 DIFFPROG || 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 + "${DIFFPROG}" "${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 + pkgbuild-summarize-nonfree $? + + ################################################################ + + 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..f0969c7 --- /dev/null +++ b/src/chroot-tools/.gitignore @@ -0,0 +1,7 @@ +makechrootpkg.sh* +arch-nspawn* +mkarchroot* +!*.patch + +chcleanup +chcleanup.lib diff --git a/src/chroot-tools/HACKING.md b/src/chroot-tools/HACKING.md new file mode 100644 index 0000000..e50aa11 --- /dev/null +++ b/src/chroot-tools/HACKING.md @@ -0,0 +1,3 @@ +Unfortunately, `makechrootpkg.sh` is GPLv2 ONLY. This means that +everything that uses it must be held to GPLv2+ instead of GPLv3+. I'm +calling this anything that gets loaded into the same process as it. diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile new file mode 100644 index 0000000..48a1631 --- /dev/null +++ b/src/chroot-tools/Makefile @@ -0,0 +1,27 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkglibexecdir = $(libexecdir)/libretools/chroot + +libretools-bins = chcleanup librechroot libremakepkg +libretools-libexecs += arch-nspawn chcleanup distcc-tool indent mkarchroot +libretools-libs += makechrootpkg.sh +devtools-files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in +am_clean_files += chcleanup.lib + +$(srcdir)/makechrootpkg.sh.in: $(srcdir)/%.sh.in: $(devtoolsdir)/%.in + cp $< $@ + +$(outdir)/chcleanup: $(srcdir)/chcleanup.in $(outdir)/chcleanup.lib + m4 -I$(@D) -P $< | $(edit) | install -m755 /dev/stdin $@ +$(outdir)/chcleanup.lib: $(call _am_path,$(topoutdir)/src/lib/common.sh) $(outdir)/Makefile + bash -c '. $<; declare -f _l plain msg msg2 error' > $@ + +$(outdir)/distcc-tool.pot: LIBREXGETTEXT_FLAGS+=--simple=errusage + +$(DESTDIR)$(bindir)/chcleanup: $(var)bindir $(var)libexecdir + mkdir -p $(@D) + ln -srfT $(DESTDIR)$(libexecdir)/libretools/chroot/chcleanup $@ + +am_depdirs = ../lib + +include $(topsrcdir)/automake.tail.mk diff --git a/src/chroot-tools/arch-nspawn.patch b/src/chroot-tools/arch-nspawn.patch new file mode 100644 index 0000000..dc4cb25 --- /dev/null +++ b/src/chroot-tools/arch-nspawn.patch @@ -0,0 +1,71 @@ +--- arch-nspawn.in 2016-05-10 13:48:14.303797115 -0400 ++++ arch-nspawn.ugly 2016-05-10 13:48:23.296957958 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # 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. +@@ -14,6 +16,8 @@ + + working_dir='' + ++files=() ++ + usage() { + echo "Usage: ${0##*/} [options] working-dir [systemd-nspawn arguments]" + echo "A wrapper around systemd-nspawn. Provides support for pacman." +@@ -22,17 +26,21 @@ + echo ' -C <file> Location of a pacman config file' + echo ' -M <file> Location of a makepkg config file' + echo ' -c <dir> Set pacman cache' ++ echo ' -f <file> Copy file from the host to the chroot' ++ echo ' -s Do not run setarch' + echo ' -h This message' + exit 1 + } + + orig_argv=("$@") + +-while getopts 'hC:M:c:' arg; do ++while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + C) pac_conf="$OPTARG" ;; + M) makepkg_conf="$OPTARG" ;; + c) cache_dir="$OPTARG" ;; ++ f) files+=("$OPTARG") ;; ++ s) nosetarch=1 ;; + h|?) usage ;; + *) error "invalid argument '%s'" "$arg"; usage ;; + esac +@@ -78,6 +86,12 @@ + [[ -n $pac_conf ]] && cp $pac_conf "$working_dir/etc/pacman.conf" + [[ -n $makepkg_conf ]] && cp $makepkg_conf "$working_dir/etc/makepkg.conf" + ++ local file ++ for file in "${files[@]}"; do ++ mkdir -p "$(dirname "$working_dir$file")" ++ cp -T "$file" "$working_dir$file" ++ done ++ + sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${cache_dirs[@]})|g" -i "$working_dir/etc/pacman.conf" + } + # }}} +@@ -92,6 +106,7 @@ + fi + + build_mount_args ++cache_dirs+=('/repo/') + copy_hostconf + + eval $(grep '^CARCH=' "$working_dir/etc/makepkg.conf") +@@ -99,6 +114,8 @@ + armv7h) CARCH=armv7l;; + esac + ++[[ -z $nosetarch ]] || unset CARCH ++ + exec ${CARCH:+setarch "$CARCH"} systemd-nspawn -q \ + -D "$working_dir" \ + --register=no \ diff --git a/src/chroot-tools/chcleanup.in b/src/chroot-tools/chcleanup.in new file mode 100644 index 0000000..04cf8ad --- /dev/null +++ b/src/chroot-tools/chcleanup.in @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2013, 2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# If you don't see m4_include(...) below, but see function definitions +# for msg() et al., then this is a generated file, and contains some +# code from librelib. The the source distribution for full copyright +# information. +# +# License: GNU GPLv3+ +# +# 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/>. + +# Performs chroot cleanup smartly, it only removes the unneeded packages or +# leaves you with a cleansystem +# +# See: HOOKPREBUILD + +set -eE + +DRYRUN=${DRYRUN:-false} + +# Statically include various library routines to avoid having +# dependencies on outside files. +[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools' +[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale' + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi +# Begin chcleanup.lib ########################################################## +m4_include(chcleanup.lib) +# End chcleanup.lib ############################################################ + +if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then + error "(chcleanup): Must be run inside of a chroot" + exit 1 +fi + +# Note: the in-chroot pkgconfdir is non-configurable, this is +# intentionally hard-coded. +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..." + +# Sync the local repo with pacman +cp /repo/repo.db /var/lib/pacman/sync/repo.db + +# Setup the temporary directory +TEMPDIR="$(mktemp --tmpdir -d ${0##*/}.XXXXXXXXXX)" +trap "rm -rf -- $(printf '%q' "$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..d181109 --- /dev/null +++ b/src/chroot-tools/distcc-tool @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +# -*- tab-width: 4; sh-basic-offset: 4 -*- +# distcc-tool + +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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 + +q0="$(printf '%q' "$0")" # quoted $0 + +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: %s COMMAND [COMMAND-ARGS]" "$q0" + 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 use `client`. + newhosts+=("$HOSTSPEC") + ;; + # GLOBAL_OPTION + --*) + # pass these through + newhosts+=("$HOSTSPEC") + ;; + # ZEROCONF + +zeroconf) + error "%s does not support the +zeroconf option" "$q0" + 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:"$q0 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 + local host port + read -r host port + socat STDIO TCP:"$host:$port" +} + +# 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:"$q0 server" & + trap "kill -- $!; rm -f -- $(printf '%q' "$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..7afb13d --- /dev/null +++ b/src/chroot-tools/hooks-chcleanup.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 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 -euE + +hook_pre_build+=("clean_chroot") + +clean_chroot() ( + set +x + local copydir=$1 + if $INCHROOT; then + cd /startdir + "$(librelib chroot/chcleanup)" + else + librechroot "${librechroot_flags[@]}" clean-pkgs + fi +) diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh new file mode 100644 index 0000000..850516b --- /dev/null +++ b/src/chroot-tools/hooks-check.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 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 -euE + +hook_check_pkgbuild+=("check_pkgbuild_nonfree") +check_pkgbuild_nonfree() { + local s=0 + sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$? + pkgbuild-summarize-nonfree $s +} + +#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..bb234b8 --- /dev/null +++ b/src/chroot-tools/hooks-distcc.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 2 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/>. + +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=$(< "$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/indent b/src/chroot-tools/indent new file mode 100755 index 0000000..0f047c6 --- /dev/null +++ b/src/chroot-tools/indent @@ -0,0 +1,57 @@ +#!/usr/bin/env perl +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +use warnings; +use strict; + +use constant BUFFER_SIZE => 40; +binmode(STDIN); +binmode(STDOUT); + +exit(1) if ($#ARGV != 0); +my $indent = $ARGV[0]; + +# 0: no indent has been printed for this line, an indent WILL need to be printed +# 1: an indent needs to be printed for this line IFF there is any more output on it +# 2: no indent (currently) needs to be printed for this line +my $print_indent = 0; + +my $buffer; +my $size; +my $c; +while (1) { + $size = sysread(STDIN, $buffer, BUFFER_SIZE); + last if ($size < 1); + for (0..$size-1) { + $c = substr($buffer, $_, 1); + if ($c eq "\n") { + syswrite(STDOUT, $indent) if ($print_indent == 0); + syswrite(STDOUT, $c, 1); + $print_indent = 0; + } elsif ($c eq "\r") { + syswrite(STDOUT, $c, 1); + $print_indent = 1 if ($print_indent == 2); + } else { + syswrite(STDOUT, $indent) if ($print_indent < 2); + syswrite(STDOUT, $c, 1); + $print_indent = 2; + } + } +} diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot new file mode 100755 index 0000000..cf564ed --- /dev/null +++ b/src/chroot-tools/librechroot @@ -0,0 +1,509 @@ +#!/usr/bin/env bash +set -euE +# librechroot + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 2 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 4 places: +# - the usage() text +# - the commands=() array +# - the case statement in main() that checks the number of arguments +# - the case statement in main() that runs them + +. "$(librelib conf)" +load_files chroot + +. libremessages + +shopt -s nullglob +umask 0022 + +################################################################################ +# Wrappers for files in ${pkglibexecdir}/chroot/ # +################################################################################ + +readonly _arch_nspawn="$(librelib chroot/arch-nspawn)" +readonly _mkarchroot="$(librelib chroot/mkarchroot)" +readonly _makechrootpkg="$(librelib chroot/makechrootpkg.sh)" + +arch_nspawn_flags=() +sysd_nspawn_flags=() + +hack_arch_nspawn_flags() { + local copydir="$1" + + local makepkg_conf="$copydir/etc/makepkg.conf" + + OPTIND=1 + set -- ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} + while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + M) makepkg_conf="$OPTARG" ;; + *) :;; + esac + done + + # Detect the architecture of the chroot + local CARCH + if [[ -f "$makepkg_conf" ]]; then + eval $(grep '^CARCH=' "$makepkg_conf") + else + CARCH="$(uname -m)" + fi + + if [[ "$CARCH" == armv7h ]] && ! setarch armv7l 2>/dev/null; then + # We're running an ARM chroot on a non-ARM processor + + # Make sure that qemu-static is set up with binfmt_misc + if [[ $(grep -xF \ + -e 'enabled'\ + -e 'interpreter /usr/bin/qemu-arm-static' \ + /proc/sys/fs/binfmt_misc/arm 2>/dev/null |wc -l) -lt 2 ]]; then + error 'Cannot cross-compile for ARM on x86' + plain 'This requires a binfmt_misc entry for qemu-arm-static,' + plain 'which is provided by the %s package.' binfmt-qemu-static + plain 'If you have this, you may need to restart %s.' systemd-binfmt.service + return 1 + fi + + # Let qemu/binfmt_misc do its thing + arch_nspawn_flags+=(-f /usr/bin/qemu-arm-static -s) + + # The -any packages are built separately for ARM from + # x86, so if we use the same CacheDir as the x86 host, + # then there will be PGP errors. + mkdir -p /var/cache/pacman/pkg-arm + arch_nspawn_flags+=(-c /var/cache/pacman/pkg-arm) + fi +} + +# Usage: arch-nspawn $copydir $cmd... +arch-nspawn() { + local copydir=$1; shift + local cmd=("$@") + + local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}) + hack_arch_nspawn_flags "$copydir" + + "$_arch_nspawn" \ + ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \ + "$copydir" \ + ${sysd_nspawn_flags+"${sysd_nspawn_flags[@]}"} \ + -- \ + "${cmd[@]}" +} + +# Usage: mkarchroot $copydir $pkgs... +mkarchroot() { + local copydir=$1; shift + local pkgs=("$@") + + local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}) + hack_arch_nspawn_flags "$copydir" + + unshare -m "$_mkarchroot" \ + ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \ + "$copydir" \ + "${pkgs[@]}" +} + +# Usage: _makechrootpkg $function $arguments... +# Don't load $_makechrootpkg directly because it doesn't work with -euE +_makechrootpkg() ( + set +euE + . "$_makechrootpkg" + "$@" +) + +################################################################################ +# Utility functions # +################################################################################ + +# 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 +} + +# Print code to 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 +} + +check_mountpoint() { + local file=$1 + local mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')" + local mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }")) + ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}" +} + +################################################################################ +# Main program # +################################################################################ + +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 variables 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 'The processor architecture of the chroot is determined + by the by `CARCH` variable in the `/etc/makepkg.conf` + file inside of the chroot.' + echo + prose 'The `-A CARCH` flag is *almost* simply an alias for' + printf ' %s\n' \ + '-C "/usr/share/pacman/defaults/pacman.conf.$CARCH" \' \ + '-M "/usr/share/pacman/defaults/makepkg.conf.$CARCH"' + prose 'However, before doing that, it actually makes a temporary copy of + `pacman.conf`, and sets the `Architecture` line to match the + `CARCH` line in `makepkg.conf`.' + 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 "-A <$(_ CARCH)>" 'Set the architecture of the copy; simply an + alias for the `-C` and `-M` flags, see above.' + 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 `/startdir/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 +) + +# Globals: $CHROOTDIR, $CHROOT, $COPY, $rootdir and $copydir +main() { + COPY=$LIBREUSER + [[ $COPY != root ]] || COPY=copy + + local mode=enter + while getopts 'n:l:NC:M:A: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");; + A) + if ! [[ -f "/usr/share/pacman/defaults/pacman.conf.$OPTARG" && -f "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" ]]; then + error 'Unsupported architecture: %s' "$OPTARG" + plain 'See the files in %q for valid architectures.' /usr/share/pacman/defaults/ + return 1; + fi + trap 'rm -f -- "$tmppacmanconf"' EXIT + tmppacmanconf="$(mktemp --tmpdir librechroot-pacman.conf.XXXXXXXXXX)" + < "/usr/share/pacman/defaults/pacman.conf.$OPTARG" sed -r "s|^#?\\s*Architecture.+|Architecture = ${OPTARG}|g" > "$tmppacmanconf" + arch_nspawn_flags+=( + -C "$tmppacmanconf" + -M "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" + );; + w) sysd_nspawn_flags+=("--bind=$OPTARG");; + r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + error "Must specify a command" + usage >&2 + return 1 + fi + mode=$1 + if ! in_array "$mode" "${commands[@]}"; then + error "Unrecognized command: %s" "$mode" + usage >&2 + return 1 + fi + shift + case "$mode" in + noop|make|sync|delete|update|enter|clean-pkgs|clean-repo) + if [[ $# -gt 0 ]]; then + error 'Command `%s` does not take any arguments: %s' "$mode" "$*" + usage >&2 + return 1 + fi + :;; + install-file) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one file' "$mode" + usage >&2 + return 1 + else + local missing=() + local file + for file in "$@"; do + if ! [[ -f $file ]]; then + missing+=("$file") + fi + done + if [[ ${#missing[@]} -gt 0 ]]; then + error "%s: file(s) not found: %s" "$mode" "${missing[*]}" + return 1 + fi + fi + :;; + install-name) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one package name' "$mode" + usage >&2 + return 1 + fi + :;; + run) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one argument' "$mode" + usage >&2 + return 1 + fi + :;; + esac + + + 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 + + # XXX: SYSTEMD-STDIN HACK + if ! [[ -t 0 ]]; then + error "Input is not a TTY" + plain "https://labs.parabola.nu/issues/431" + plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290" + prose "Due to a bug in systemd-nspawn, redirecting stdin is not + supported." >&2 + return 1 + fi + + # Keep this lock for 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 [[ $mode != delete ]]; then + if ! check_mountpoint "$copydir.lock"; then + error "Chroot copy is mounted with nosuid or noexec options: [%s]" "$COPY" + return 1 + fi + + if [[ ! -d $rootdir ]]; then + msg "Creating 'root' copy for chroot [%s]" "$CHROOT" + mkarchroot "$rootdir" base-devel + 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 + + # Note: the in-chroot pkgconfdir is non-configurable, this is + # intentionally hard-coded. + mkdir -p "$copydir/etc/libretools.d" + { + if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then + echo 'CHROOTEXTRAPKG=()' + else + printf 'CHROOTEXTRAPKG=(' + printf '%q ' "${CHROOTEXTRAPKG[@]}" + printf ')\n' + fi + } > "$copydir"/etc/libretools.d/chroot.conf + + # "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 + trap EXIT # clear the trap to remove the tmp pacman.conf from -A + 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" + 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 -- $(printf '%q ' "$copydir"/{bin/chcleanup,chrootexec})" EXIT + install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup" + printf '%s\n' \ + '#!/bin/bash' \ + 'mkdir -p /startdir' \ + 'cd /startdir' \ + '/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..a226e38 --- /dev/null +++ b/src/chroot-tools/libremakepkg @@ -0,0 +1,289 @@ +#!/usr/bin/env bash +set -euE +# libremakepkg + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 2 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)" + +set -o pipefail +shopt -s nullglob +umask 0022 + +# Global variables: +readonly _indent="$(librelib chroot/indent)" +readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false) +NONET=true # can be changed with the -N flag +# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf +# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf +# LIBREUSER, LIBREHOME are set by conf.sh +librechroot_flags=() + +# 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 ##################################################### + +indent() { + "$_indent" ' | ' +} + +# 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=() + for hook in "${!hookvar}"; do + "$hook" "$@" || fails+=("$hook") + done |& indent + + if [[ ${#fails[@]} -gt 0 ]]; then + error "Failure(s) in %s: %s" "$hookname" "${fails[*]}" + return 1 + else + return 0 + fi +} + +# 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 +} + +hook_post_build+=('cleanup') +cleanup() { + local copydir=$1 + rm -f -- "$copydir"/chroot{prepare,build} +} + +build() ( + local copydir=$1 + local repack=$2 + local makepkg_args=("${@:3}") + + local run_ynet=() + local run_nnet=() + if $INCHROOT; then + run_ynet=(unshare) + run_nnet=(unshare -n) + else + run_ynet=(librechroot "${librechroot_flags[@]}" run) + run_nnet=(librechroot "${librechroot_flags[@]}" -N run) + fi + $NONET || run_nnet=("${run_ynet[@]}") + + prepare_chroot "$copydir" "$LIBREHOME" "$repack" false + "${run_ynet[@]}" /chrootprepare "${makepkg_args[@]}" |& indent + run_hook pre_build "$copydir" + trap "run_hook post_build $(printf '%q' "$copydir")" EXIT + "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" |& indent +) + +# The main program ############################################################# + +usage() { + print "Usage: %s [options]" "${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 `{PKG,SRC,SRCPKG,LOG}DEST` 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:' + print ' %s options:' librechroot + flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' + flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use' + flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only' + print ' %s options:' libremakepkg + 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:w:r: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;; + w|r) if $INCHROOT; then err_chflag "$flag"; else + librechroot_flags+=(-$flag "$OPTARG"); fi;; + N) NONET=false;; + R) repack=true; makepkg_args+=(-R);; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# != 0 ]]; then + error 'Extra arguments: %s' "$*" + return 1 + fi + + # 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 + + # Load makepkg configuration ########################################### + # Note that all of these are globals + PKGDEST="$(get_var makepkg PKGDEST "$PWD")" + SRCDEST="$(get_var makepkg SRCDEST "$PWD")" + SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")" + LOGDEST="$(get_var makepkg LOGDEST "$PWD")" + MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')" + PACKAGER="$(get_var makepkg PACKAGER '')" + + # 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 + + # Make sure that the various *DEST directories exist + mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST" + + # OK, we are starting now ############################################## + + if $INCHROOT; then + lock 9 "/build/.lock" \ + "Waiting for existing lock on build directory to be released" + else + librechroot_flags+=( + -r "$PWD:/startdir_host" + -r "$SRCDEST:/srcdest_host" + -n "$CHROOT" + -l "$copy" + ) + + # 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 + msg 'Initializing the chroot...' + librechroot "${librechroot_flags[@]}" make |& indent + 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_var makepkg CARCH)" + + # Pre-build + msg 'Starting pre-build activities...' + run_hook check_pkgbuild + msg 'Downloading sources...' + download_sources "$copydir" "$LIBREUSER" |& indent + + # Build + msg 'Starting to build the package...' + trap "exit_copy '$copydir' '$LIBREUSER'" EXIT + build "$copydir" "$repack" "${makepkg_args[@]}" + + # Post-build + msg 'Starting post-build activities...' + run_hook check_pkg + add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent +} + +main "$@" diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch new file mode 100644 index 0000000..d5c6fca --- /dev/null +++ b/src/chroot-tools/makechrootpkg.sh.patch @@ -0,0 +1,342 @@ +--- makechrootpkg.sh.in 2016-02-08 17:17:11.848386338 -0500 ++++ makechrootpkg.sh.ugly 2016-04-14 22:01:05.923288181 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # 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. +@@ -12,6 +14,7 @@ + + shopt -s nullglob + ++init_variables() { + default_makepkg_args=(-s --noconfirm -L --holdver) + makepkg_args=("${default_makepkg_args[@]}") + repack=false +@@ -29,9 +32,10 @@ + bindmounts_rw=() + + 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]" +@@ -68,28 +72,51 @@ + } + + # {{{ functions ++# Usage: load_vars $makepkg_conf ++# Globals: ++# - SRCDEST ++# - SRCPKGDEST ++# - PKGDEST ++# - LOGDEST ++# - MAKEFLAGS ++# - PACKAGER + load_vars() { + local makepkg_conf="$1" var + + [[ -f $makepkg_conf ]] || return 1 + + for var in {SRC,SRCPKG,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 ++ ++ if [[ "$chrootdir/root" -ef "$copydir" ]]; then ++ error 'Cannot sync copy with itself: %s' "$copydir" ++ return 1 ++ 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" ++ slock 8 "$chrootdir/root.lock" \ ++ "Locking clean chroot [%s]" "$chrootdir/root" + +- stat_busy "Creating clean working copy [%s]" "$copy" ++ stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir" + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + if [[ -d $copydir ]]; then + btrfs subvolume delete "$copydir" >/dev/null || +@@ -105,14 +132,18 @@ + + # Drop the read lock again + lock_close 8 +- fi + + # Update mtime + touch "$copydir" + } + +-clean_temporary() { +- stat_busy "Removing temporary copy [%s]" "$copy" ++# Usage: delete_chroot $copydir ++delete_chroot() { ++ local copydir=$1 ++ # Detect chrootdir filesystem type ++ local chroottype=$(stat -f -c %T "$copydir") ++ ++ stat_busy "Removing chroot copy [%s]" "$copydir" + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + btrfs subvolume delete "$copydir" >/dev/null || + die "Unable to delete subvolume %s" "$copydir" +@@ -127,9 +158,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" +@@ -142,11 +178,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" +@@ -193,12 +237,12 @@ + printf 'builduser:x:%d:100:builduser:/:/usr/bin/nologin\n' "$builduser_uid" >>"$copydir/etc/passwd" + chown -R "$builduser_uid" "$copydir"/{build,pkgdest,srcpkgdest,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 +@@ -211,10 +255,25 @@ + chmod 440 "$copydir/etc/sudoers.d/builduser-pacman" + fi + ++ if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then ++ local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q') ++ local ins='[repo] ++SigLevel = Optional TrustAll ++Server = file:///repo ++' ++ sed -i "${line}i${ins//$'\n'/\\n}" "$copydir/etc/pacman.conf" ++ fi ++ + # This is a little gross, but this way the script is recreated every time in the + # working copy + { + printf '#!/bin/bash\n' ++ declare -f _chrootprepare ++ printf '_chrootprepare "$@"\n' ++ } > "$copydir/chrootprepare" ++ chmod +x "$copydir/chrootprepare" ++ { ++ printf '#!/bin/bash\n' + declare -f _chrootbuild + printf '_chrootbuild "$@" || exit\n' + +@@ -231,13 +290,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" +@@ -247,10 +312,10 @@ + (( $? != 0 )) && die "Could not download sources." + + # Clean up garbage from verifysource +- rm -rf $builddir ++ rm -rf "$builddir" + } + +-_chrootbuild() { ++_chrootprepare() { + # This function isn't run in makechrootpkg, + # so no global variables + +@@ -259,6 +324,7 @@ + shopt -s nullglob + + # XXX: Workaround makepkg disliking read-only dirs ++ rm -rf -- /srcdest/* /startdir/* + ln -sft /srcdest /srcdest_host/* + ln -sft /startdir /startdir_host/* + +@@ -288,15 +354,42 @@ + exit 1 + fi + +- sudo -u builduser makepkg "$@" ++ # Sync deps now, as networking may be disabled during _chrootbuild ++ cp /repo/repo.db /var/lib/pacman/sync/repo.db ++ sudo -u builduser makepkg "$@" --nobuild ++} ++ ++_chrootbuild() { ++ # This function isn't run in makechrootpkg, ++ # so no global variables ++ ++ . /etc/profile ++ export HOME=/build ++ shopt -s nullglob ++ ++ cd /startdir ++ ++ sudo -u builduser makepkg "$@" --noextract --noprepare + } + ++# 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 + [[ $l == */logpipe.* ]] && continue + chown "$src_owner" "$l" +@@ -310,6 +403,9 @@ + } + # }}} + ++main() { ++init_variables ++ + orig_argv=("$@") + + while getopts 'hcur:I:l:nTD:d:' arg; do +@@ -375,30 +471,45 @@ + [[ -d $SRCPKGDEST ]] || SRCPKGDEST=$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" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + 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 + +-download_sources ++download_sources "$copydir" "$src_owner" + +-prepare_chroot ++prepare_chroot "$copydir" "$USER_HOME" "$repack" + + if arch-nspawn "$copydir" \ + --bind-ro="$PWD:/startdir_host" \ + --bind-ro="$SRCDEST:/srcdest_host" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ ++ /chrootprepare "${makepkg_args[@]}" && ++ arch-nspawn "$copydir" \ ++ --bind-ro="$PWD:/startdir_host" \ ++ --bind-ro="$SRCDEST:/srcdest_host" \ ++ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + /chrootbuild "${makepkg_args[@]}" + then +- move_products ++ move_products "$copydir" "$src_owner" + else + (( ret += 1 )) + fi + +-$temp_chroot && clean_temporary ++$temp_chroot && delete_chroot "$copydir" + + if (( ret != 0 )); then + if $temp_chroot; then +@@ -409,3 +520,4 @@ + else + true + fi ++} diff --git a/src/chroot-tools/mkarchroot.patch b/src/chroot-tools/mkarchroot.patch new file mode 100644 index 0000000..3882ec6 --- /dev/null +++ b/src/chroot-tools/mkarchroot.patch @@ -0,0 +1,67 @@ +--- mkarchroot.in 2016-04-15 17:38:00.221067734 -0400 ++++ mkarchroot.ugly 2016-05-09 22:36:18.284175885 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # 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. +@@ -14,23 +16,29 @@ + + working_dir='' + ++files=() ++ + usage() { + echo "Usage: ${0##*/} [options] working-dir package-list..." + echo ' options:' + echo ' -C <file> Location of a pacman config file' + echo ' -M <file> Location of a makepkg config file' + echo ' -c <dir> Set pacman cache' ++ echo ' -f <file> Copy file from the host to the chroot' ++ echo ' -s Do not run setarch' + echo ' -h This message' + exit 1 + } + + orig_argv=("$@") + +-while getopts 'hC:M:c:' arg; do ++while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + C) pac_conf="$OPTARG" ;; + M) makepkg_conf="$OPTARG" ;; + c) cache_dir="$OPTARG" ;; ++ f) files+=("$OPTARG") ;; ++ s) nosetarch=1 ;; + h|?) usage ;; + *) error "invalid argument '%s'" "$arg"; usage ;; + esac +@@ -68,6 +76,16 @@ + chmod 0755 "$working_dir" + fi + ++for file in "${files[@]}"; do ++ mkdir -p "$(dirname "$working_dir$file")" ++ cp "$file" "$working_dir$file" ++done ++ ++_env=() ++while read -r varname; do ++ _env+=("$varname=${!varname}") ++done < <(declare -x | sed -r 's/^declare -x ([^=]*)=.*/\1/' | grep -i '_proxy$') ++env -i "${_env[@]}" \ + pacstrap -GMcd ${pac_conf:+-C "$pac_conf"} "$working_dir" \ + "${cache_dirs[@]/#/--cachedir=}" "$@" || die 'Failed to install all packages' + +@@ -75,7 +93,8 @@ + echo 'LANG=C' > "$working_dir/etc/locale.conf" + echo "$CHROOT_VERSION" > "$working_dir/.arch-chroot" + +-exec arch-nspawn \ ++exec "$(librelib chroot/arch-nspawn)" \ ++ ${nosetarch:+-s} \ + ${pac_conf:+-C "$pac_conf"} \ + ${makepkg_conf:+-M "$makepkg_conf"} \ + ${cache_dir:+-c "$cache_dir"} \ diff --git a/src/dagpkg b/src/dagpkg new file mode 100755 index 0000000..1989b4d --- /dev/null +++ b/src/dagpkg @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# +# dagpkg - create a directed graph of package dependencies and build +# them in topological order + +# Copyright (C) 2014 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2014 Michał Masłowski <mtjm@mtjm.eu> +# +# License: GNU GPLv3+ +# +# 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 + +source libremessages +source "$(librelib conf)" + +# Source variables from libretools +load_files libretools +check_vars libretools FULLBUILDCMD || exit 1 +#check_vars libretools HOOKPREBUILD HOOKLOCALRELEASE || exit 1 # optional + +# Source variables from makepkg +load_files makepkg +check_vars makepkg CARCH || exit 1 + +# Globals: +# - temp_dir +# - log +# - name (sort of, it's also local to visit_pkgbuild() ) +# - prev +# - I +# - marks +# - various PKGBUILD variables: +# - pkgbase/pkgname +# - epoch/pkgver/pkgrel +# - arch +# - {,make,check}depends + +# End inmediately but print an useful message +trap_exit() { + local signal=$1; shift + local msg=("$@") + term_title "error!" + echo + error "(%s) %s (leftovers on %s)" \ + "${0##*/}" "$(print "${msg[@]}")" "${temp_dir}" + trap -- "$signal" + kill "-$signal" "$$" +} + +setup_traps trap_exit + +source_pkgbuild() { + # Source this PKGBUILD, if it doesn't exist, exit + if ! load_PKGBUILD &>/dev/null; then + error "No PKGBUILD in %s" "$PWD" + exit 1 + fi + + # Save resources + # This is intentionally less exhaustive than unset_PKGBUILD() + # XXX: document which things we actually *want* to not be unset. + unset pkgdesc license groups backup install md5sums sha1sums \ + sha256sums source options &>/dev/null + + unset build package &>/dev/null + + local _pkg + for _pkg in "${pkgname[@]}"; do + unset "package_${_pkg}" &>/dev/null || true + done + + # This is the name of the package + name="${pkgbase:-${pkgname[0]}}" +} + +source_pkgbuild + +# A temporary work dir and log file +temp_dir="${1:-$(mktemp -dt ${name}-testpkg-XXXX)}" +log="${temp_dir}/buildorder" + +# Mark array for DFS-based topological sort. See +# https://en.wikipedia.org/wiki/Topological_sort for an explanation of +# the algorithm. Key: package name, value: 0 for unvisited package, 1 +# during visit, 2 after visit. +declare -A marks + +# Visit a PKGBUILD for graph building. +visit_pkgbuild() { + # The name of the previous package + prev="${1}" + + local name + source_pkgbuild + + # If it's already built we don't bother + if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then + return + fi + + # Detect cycle or already visited package + case "${marks[$name]:-0}" in + 1) msg2 "cycle found with %s depending on %s" $prev $name + exit 1;; + 2) return;; + esac + + msg "%s (%s)" ${name} ${prev} + + if ! in_array "${CARCH}" "${arch[@]}"; then + warning "%s isn't ported to %s yet" ${name} ${CARCH} + fi + + # If the envvar I contains this package, ignore it and exit + if in_array "$name" $I; then + msg2 "%s ignored" ${name} + return + fi + + # Mark the package as being visited + marks[$name]=1 + + # Recurse into dependencies + local d + for d in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}"; do + # Cleanup dependency versions + d=$(echo $d | sed "s/[<>=].*//") + + # Where's the pkgbuild? + local w=$(toru-where $d) + + # Skip if not available + test -z "$w" && continue + + # Go to this dir + pushd $w &>/dev/null + + visit_pkgbuild "$name" + + popd &>/dev/null + done + + # Mark the package as finished + marks[$name]=2 + # Append it to the reversed list of packages to build. + echo "$name" >> "${log}" +} + +# If we specified a work dir on the cli it means we want to skip +# dependency graph creation and jump to build whatever is there +if [ -z "${1}" ]; then + # Visit the root PKGBUILD to make the graph. + visit_pkgbuild "" +else + msg "Resuming build..." +fi + +# enter work dir +pushd "${temp_dir}" &>/dev/null +nl ${log} | while read order pkg; do + # skip if already built + if test -f "${pkg}/built_ok"; then + warning "tried to build %s twice" "%{pkg}" + continue + fi + + # where's this package? + local w="$(toru-where "$pkg")" + test -z "$w" && continue + + # copy to work dir if not already + # this means you can make modifications to the pkgbuild during the + # graph build or remove the dir after a build failure and let dagpkg + # copy a new version + test -d "$pkg" || cp -r "$w" "$pkg" + pushd "$pkg" &>/dev/null + + term_title "%s(%s)" "$pkg" "$order" + + msg "Building %s" ${pkg} + + # upgrade the system + # this would probably have to go on HOOKPREBUILD if you're working + # outside chroots + sudo -E pacman -Syu --noconfirm + + # run the pre build command from libretools.conf + if [[ -n "$HOOKPREBUILD" ]]; then + ${HOOKPREBUILD} + fi + + # run the build command + ${FULLBUILDCMD} + + # Run local release hook with $1 = $repo + if [[ -n "$HOOKLOCALRELEASE" ]]; then + ${HOOKLOCALRELEASE} "$(basename "$(dirname "$w")")" + fi + + # it's built! + touch built_ok + + popd &>/dev/null +done + +popd &>/dev/null +# cleanup +rm -rf ${log} "${temp_dir}" + +term_title "done" diff --git a/src/devtools/.gitignore b/src/devtools/.gitignore new file mode 100644 index 0000000..d669bbd --- /dev/null +++ b/src/devtools/.gitignore @@ -0,0 +1,4 @@ +* +!Makefile +!.gitignore +!*.patch diff --git a/src/devtools/Makefile b/src/devtools/Makefile new file mode 100644 index 0000000..72907f0 --- /dev/null +++ b/src/devtools/Makefile @@ -0,0 +1,12 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-bins += checkpkg find-libdeps finddeps lddd +devtools-files = $(addsuffix .in,$(libretools-bins)) +am_sys_files += $(bindir)/find-libprovides + +$(DESTDIR)$(bindir)/find-libprovides: + install -d $(@D) + ln -sf find-libdeps $@ + +include $(topsrcdir)/automake.tail.mk diff --git a/src/devtools/checkpkg.patch b/src/devtools/checkpkg.patch new file mode 100644 index 0000000..5cfb794 --- /dev/null +++ b/src/devtools/checkpkg.patch @@ -0,0 +1,45 @@ +--- checkpkg.in 2014-03-21 13:59:31.849658036 -0400 ++++ checkpkg.ugly 2014-03-21 14:20:52.340291982 -0400 +@@ -1,8 +1,30 @@ + #!/bin/bash ++# License: Unspecified + + shopt -s extglob + +-m4_include(lib/common.sh) ++. "$(librelib messages)" ++ ++usage() { ++ print 'Usage: %s [-h]' "${0##*/}" ++ print 'Compare a locally built a package with the one in the repositories.' ++ echo ++ prose 'This should be run from a directory containing a ++ PKGBUILD. It searches for a locally built package ++ corresponding to the PKGBUILD, and downloads the last ++ version of that package from the pacman repositories. ++ It then compares the list of .so files provided by each ++ version of the package. It does this for each part of ++ a split package.' ++} ++ ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++elif [[ $# -gt 0 ]]; then ++ usage >&2 ++ exit 1 ++fi + + # Source makepkg.conf; fail if it is not found + if [[ -r '/etc/makepkg.conf' ]]; then +@@ -17,7 +39,9 @@ + fi + + if [[ ! -f PKGBUILD ]]; then +- die 'This must be run in the directory of a built package.' ++ error 'This must be run in the directory of a built package.' ++ usage >&2 ++ exit 1 + fi + + . ./PKGBUILD diff --git a/src/devtools/find-libdeps.patch b/src/devtools/find-libdeps.patch new file mode 100644 index 0000000..1bcffc9 --- /dev/null +++ b/src/devtools/find-libdeps.patch @@ -0,0 +1,47 @@ +--- find-libdeps.in 2014-03-21 13:59:32.059649315 -0400 ++++ find-libdeps.ugly 2014-03-21 14:21:18.955744982 -0400 +@@ -1,6 +1,7 @@ + #!/bin/bash ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" + + set -e + shopt -s extglob +@@ -19,12 +20,32 @@ + *) die "Unknown mode %s" "$script_mode" ;; + esac + ++usage() { ++ print "Usage: find-lib(deps|provides) [options] <package file|extracted package dir>" ++ print "Find library dependencies or provides of a package." ++ echo ++ prose 'Prints a list of library dependencies in the format:' ++ echo ++ print ' <soname>=<soversion>-<soarch>' ++ echo ++ prose 'Where <soversion> is the shared library version, or ++ <soname> repeated if there is no version attached; and ++ <soarch> is the architecture of the library (either `32` ++ or `64`, based on the ELF Class).' ++ echo ++ print "Options:" ++ flag "--ignore-internal" "Ignore internal libraries; libraries ++ without a version attached" ++ flag "-h" "Show this message" ++} + if [[ -z $1 ]]; then +- echo "${0##*/} [options] <package file|extracted package dir>" +- echo "Options:" +- echo " --ignore-internal ignore internal libraries" ++ usage >&2 + exit 1 + fi ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++fi + + if [[ -d $1 ]]; then + pushd $1 >/dev/null diff --git a/src/devtools/finddeps.patch b/src/devtools/finddeps.patch new file mode 100644 index 0000000..caf2121 --- /dev/null +++ b/src/devtools/finddeps.patch @@ -0,0 +1,35 @@ +--- finddeps.in 2014-03-21 13:59:32.249641424 -0400 ++++ finddeps.ugly 2014-03-21 14:21:09.949489174 -0400 +@@ -2,19 +2,26 @@ + # + # finddeps - find packages that depend on a given depname + # ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" + + match=$1 + ++usage() { ++ print 'Usage: %s <depname>' "${0##*/}" ++ print 'Find packages that depend on a given depname.' ++ echo ++ prose 'Run this script from the top-level directory of your ABS tree.' ++} + if [[ -z $match ]]; then +- echo 'Usage: finddeps <depname>' +- echo '' +- echo 'Find packages that depend on a given depname.' +- echo 'Run this script from the top-level directory of your ABS tree.' +- echo '' ++ usage >&2 + exit 1 + fi ++if [[ $match = '-h' ]]; then ++ usage ++ exit 0 ++fi + + find . -type d | while read d; do + if [[ -f "$d/PKGBUILD" ]]; then diff --git a/src/devtools/lddd.patch b/src/devtools/lddd.patch new file mode 100644 index 0000000..6383aa4 --- /dev/null +++ b/src/devtools/lddd.patch @@ -0,0 +1,29 @@ +--- lddd.in 2014-03-21 13:59:32.419634364 -0400 ++++ lddd.ugly 2014-03-21 14:21:31.538503947 -0400 +@@ -2,8 +2,25 @@ + # + # lddd - find broken library links on your machine + # ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" ++ ++usage() { ++ print "Usage: %s [-h]" "${0##*/}" ++ print "Find broken library links on your machine." ++ echo ++ prose 'Scans $PATH and library directories for ELF files with ++ references to missing shared libraries.' ++} ++ ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++elif [[ $# -gt 0 ]]; then ++ usage >&2 ++ exit 1 ++fi + + ifs=$IFS + IFS="${IFS}:" diff --git a/src/gitget/Makefile b/src/gitget/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/gitget/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/gitget/gitget b/src/gitget/gitget new file mode 100755 index 0000000..4d127c7 --- /dev/null +++ b/src/gitget/gitget @@ -0,0 +1,221 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 + +# from makepkg +dir_is_empty() { + ( + shopt -s dotglob nullglob + files=("$1"/*) + (( ${#files} == 0 )) + ) +} + +# from makepkg +cd_safe() { + if ! cd "$1"; then + error "Failed to change to directory %s" "$1" + plain "Aborting..." + exit 1 + fi +} + +# from makepkg +download_git_checkout() { + local url=$1 + local ref=$2 + local dir=$3 + local name=$4 + local push=${5:-} + + if [[ ! -d "$dir/.git" ]] ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + cd_safe "$dir" + if [[ -n $push ]]; then + git config remote.origin.pushUrl "$push" + fi + git checkout "$ref" + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + if $FORCE; then + git config remote.origin.url "$url" + else + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! { git fetch --all -p && git checkout "$ref" && git pull origin "$ref"; } ; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${repo}" "git" + fi + fi +} + +# from makepkg +download_git_bare() { + local url=$1 + local dir=$2 + local name=$3 + local push=${4:-} + + if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone --mirror "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]]; then + cd_safe "$dir" + git config remote.origin.pushUrl "$push" + fi + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! git fetch --all -p; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${name}" "git" + fi + fi +} + +usage() { + print 'Usage: %s [OPTIONS] [bare|checkout] URL DIRECTORY' "${0##*/}" + print 'A URL-handler for git urls. Capable of updating or cloning.' + echo + prose "Clones or pulls from the git URL, to a local DIRECTORY. If + \`bare\` is specified, it will create a bare repository; if + \`checkout\` is specified, it will create or update a working tree." + echo + prose 'For a checkout, the tree to checkout is specified by appending + `#ANYTHING=TREE-ISH` to the URL. For example, `#branch=stable`, + or `#tag=v12.3`. Whatever is on the left side of the equal sign + is ignored, this is for compatibility with `makepkg` source + URLs.' + echo + prose "The URL may be prefixed with \`git+\`. This is also for + compatibility with \`makepkg\` source URLs." + echo + prose "It does safety checks, figures out whether to clone or pull, and + other helpful things. This exists because the same + \`download_git\` function from makepkg was being copied and + pasted again and again." + echo + print "Options:" + flag '-f' \ + 'Instead of checking to make sure configured URLs match, force + the update, and set the URLs.' + flag "-p $(_ URL)" \ + 'In addition to setting or checking `remotes.origin.url`, also + set or check `remotes.origin.pushUrl`' + flag "-n $(_ NAME)" \ + 'In messages, instead of using the basename of DIRECTORY as the + repository name, use NAME' + flag '-h' 'Show this message' +} + +FORCE=false +main() { + local push='' + local name='' + while getopts 'fp:n:h' flag; do + case "${flag}" in + f) FORCE=true;; + p) push=$OPTARG;; + n) name=$OPTARG;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + [[ $# == 3 ]] || { usage >&2; return 1; } + local mode=$1 + local url=${2#git+} + local dir=$3 + + local urlmain=${url%%#*} + local urlfrag=${url#*#} + if [[ "$urlfrag" == "$urlmain" ]]; then + urlfrag='' + fi + local ref=${urlfrag#*=} + + if [[ -z $ref ]]; then + ref=master + fi + + name=${name:-${dir##*/}} + + case "$mode" in + checkout) download_git_checkout "$urlmain" "$ref" "$dir" "$name" "$push";; + bare) download_git_bare "$urlmain" "$dir" "$name" "$push";; + *) usage >&2; return 1;; + esac +} + +main "$@" diff --git a/src/gitget/libregit b/src/gitget/libregit new file mode 100755 index 0000000..e45c3a8 --- /dev/null +++ b/src/gitget/libregit @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 + +usage() { + print 'Usage: %s REPO REF DIR' "${0##*/}" + print 'A compatibility wrapper around `gitget checkout`' + echo + prose "This exists because gitget used to be called libregit, and took + the arguments in this format, and I'm sure there are a few + scripts floating around that use it." + echo + prose "Clones or pulls from the git URL '<REPO>', and checks out the git + ref '<REF>' to the directory '<DIR>'." +} + +main() { + [[ $# == 3 ]] || { usage >&2; return 1; } + repo=$1 + ref=$2 + dir=$3 + + gitget checkout "${repo}#ref=${ref}" "${dir}" +} + +main "$@" diff --git a/src/is_built b/src/is_built new file mode 100755 index 0000000..29b2744 --- /dev/null +++ b/src/is_built @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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 + +usage() { + print "Usage: %s [-h] PKGNAME [PKGVER]" "${0##*/}" "${0##*/}" + print 'Detect iv a given package (version) is already in the repos' + echo + prose "If a version is specified, it assumed that you want a greater or + equal version." + echo + prose "Example usage:" + print " $ %s 'pcre' '20'" "${0##*/}" + echo + print "Exit status:" + print " 0: The package is built" + print " 1: The package has not built" + print " >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..650c85f --- /dev/null +++ b/src/lib/.gitignore @@ -0,0 +1,3 @@ +common.sh +common.sh.in +conf.sh diff --git a/src/lib/HACKING.md b/src/lib/HACKING.md new file mode 100644 index 0000000..8bebaf6 --- /dev/null +++ b/src/lib/HACKING.md @@ -0,0 +1,15 @@ +Special stuff about hacking ih the /src/lib directory: + + - Everything should be GPLv2 AND GPLv3 compatible. No GPLv3 only. + - Name a file `libre${NAME}` if it should be executable directly, or + `${name}.sh` if it should only be available to be sourced. + - When printing a message that is internal to /src/lib, and not part + of the programm calling the library; prefix the print command with + `_l`. `_l()` is defined in `common.sh` (and `librelib`, since it + cannot use any libraries itself). + - When changing the message functions, be aware that some are + duplicated in: + * /src/chroot-tools/chcleanup + * /src/chroot-tools/distcc-tool + * /src/lib/librelib + And that they probably need to be updated as well. diff --git a/src/lib/Makefile b/src/lib/Makefile new file mode 100644 index 0000000..9e9b4a8 --- /dev/null +++ b/src/lib/Makefile @@ -0,0 +1,45 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-libs += common.sh conf.sh +devtools-files = common.sh.in + +# Build ############################################################## + +$(outdir)/conf.sh: $(var)sysconfdir $(var)pkgconfdir + +$(outdir)/common.sh: $(outdir)/%: $(srcdir)/%.in $(srcdir)/%.head $(srcdir)/%.tail $(outdir)/Makefile + @echo "OUT $@" + @{ \ + cat '$(<D)/$*.head' && \ + echo && \ + sed -r \ + -e '/encoding problem/d;/LANG=/d' \ + -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' \ + -e 's/gettext /_l _ /g' \ + -e "s/^(\s+)(msg|error) '/\1_l \2 '/" \ + -e 's|lock\(\)\s*\{|lock()\n{|' \ + '$(<D)/$*.in' && \ + echo && \ + cat '$(<D)/$*.tail' && \ + :; } > '$@' + +# Translate ########################################################## + +$(outdir)/blacklist.sh.pot: $(srcdir)/blacklist.sh $(srcdir)/librexgettext + @echo "OUT $@" + @{ \ + sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | \ + sed -r -e 's/^# (.*)/msgid "\1"\nmsgstr ""\n/' \ + -e 's|^[0-9]*$$|#. embedded usage text\n#: $<:&|' && \ + $(<D)/librexgettext --simple=_l:2 $< && \ + :; } | $(pofmt) > $@ +$(outdir)/common.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/conf.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/librelib.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/messages.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/librexgettext.pot: LIBREXGETTEXT_FLAGS += --simple=errusage + +###################################################################### + +include $(topsrcdir)/automake.tail.mk diff --git a/src/lib/blacklist.sh b/src/lib/blacklist.sh new file mode 100644 index 0000000..0a3cc39 --- /dev/null +++ b/src/lib/blacklist.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` + +# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +# 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 '/^#/d' -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" + + _l 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 + _l warning "Using local copy of blacklist" + else + _l 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- +} diff --git a/src/lib/blacklist.sh.3.ronn b/src/lib/blacklist.sh.3.ronn new file mode 120000 index 0000000..9001445 --- /dev/null +++ b/src/lib/blacklist.sh.3.ronn @@ -0,0 +1 @@ +libreblacklist.1.ronn
\ No newline at end of file diff --git a/src/lib/common.sh.3.ronn b/src/lib/common.sh.3.ronn new file mode 100644 index 0000000..30003e0 --- /dev/null +++ b/src/lib/common.sh.3.ronn @@ -0,0 +1,16 @@ +common.sh -- common Bash routines from devtools +=============================================== + +## SYNOPSIS + +`. "$(librelib common)"` + +## DESCRIPTION + +In short, don't use this. Use `libremessages`(1) instead. +`libremessages` uses this internally. + +## SEE ALSO + + * librelib(7) + * libremessages(1)/messages.sh(3) diff --git a/src/lib/common.sh.head b/src/lib/common.sh.head new file mode 100644 index 0000000..23bfeb8 --- /dev/null +++ b/src/lib/common.sh.head @@ -0,0 +1,25 @@ +#!/hint/bash +# This may be included with or without `set -euE` + +# This file is included by libremessages. +# You should probably use libremessages instead of this. + +# License: Unspecified + +shopt -s extglob + +if [[ -z ${_INCLUDE_COMMON_SH:-} ]]; then +_INCLUDE_COMMON_SH=true + +[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools' +[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale' + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} diff --git a/src/lib/common.sh.tail b/src/lib/common.sh.tail new file mode 100644 index 0000000..e133fad --- /dev/null +++ b/src/lib/common.sh.tail @@ -0,0 +1 @@ +fi diff --git a/src/lib/conf.sh.3.ronn b/src/lib/conf.sh.3.ronn new file mode 100644 index 0000000..0974bdb --- /dev/null +++ b/src/lib/conf.sh.3.ronn @@ -0,0 +1,126 @@ +conf.sh(3) -- easy loading of configuration files +================================================= + +## SYNOPSIS + +`. "$(librelib conf)"` + +## DESCRIPTION + +`conf.sh` is a Bash(1) library to easily load various configuration +files related to Arch Linux/Parabola(7) and libretools(7). + +### VARIABLES + +When loading configuration files in a program run with `sudo`(8), it +is often desirable to load the configuration files from the home +directory of the user who called `sudo`, instead of from /root. + +To accommodate this, instead of using the usual $<USER> and $<HOME>, +`conf.sh` sets $<LIBREUSER> and $<LIBREHOME>, which it then uses. + + * <LIBREUSER>: + If $<SUDO_USER> is set, then $<LIBREUSER> is set to + that. Otherwise, $<LIBREUSER> is set to the value of $<USER>. + * <LIBREHOME>: + If $<LIBREUSER> == $<USER>, then $<LIBREHOME> is set to the value + of $<HOME>. Otherwise, it is set to the default home directory + of the user $<LIBREUSER>. + +Further, `conf.sh` works with XDG; it sets and uses +$<XDG_CONFIG_HOME> and $<XDG_CACHE_HOME> with the following values: + + * <XDG_CONFIG_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.config" and + exported. + * <XDG_CACHE_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.cache" and + exported. + +Note that only the XDG_* variables are exported. + +### GENERIC ROUTINES + +The following routines take a "slug" to refer to a group of +configuration files; that is the basename of the files. For example, +<SLUG>='abs' will identify `/etc/abs.conf` and `$<LIBREHOME>/.abs.conf`. + +The routines you will likely actually use are: + + * `load_files` <SLUG>: + Loads the configuration files for <SLUG>, loading the files in + the correct order, and checking the appropriate environmental + variables. + * `check_vars` <SLUG> <VARS>...: + Checks to see if all of <VARS> are defined. If any of them + aren't, it prints a message telling the user to configure them in + the configuration files for <SLUG>, and returns with a non-zero + status. + * `get_var` <SLUG> <VAR> <DEFAULT>: + If <VAR> is set in the configuration for <SLUG>, print its + value, considering environmental variables. If it is not set, + return <DEFAULT>. This does NOT work for array variables. + * `set_var` <SLUG> <VAR> <VALUE>: + Set the variable <VAR> equal to <VALUE> in the configuration file + for <SLUG> of highest precedence that already exists and is + writable. If no files fit this description, the routine does + nothing and returns a non-zero exit status. This does NOT work + for array variables. + +There are two more routines the above routines use internally that are +used internally by by the above routines. You are unlikely to use +them directly, but they might be useful for debugging, or at least +describing behavior. + + * `list_files` <SLUG>: + Lists (newline-separated) the configuration files that must be + considered for <SLUG>. Files listed later take precedence over + files listed earlier. + * `list_envvars` <SLUG>: + Lists (newline-separated) the environmental variables that take + precedence over the settings in the configuration files for + <SLUG>. For example, in `makepkg.conf`(8) (<SLUG>=makepkg), if the + <PACKAGER> environmental variable is set, the value in the + configuration file is ignored. + +### PKGBUILD ROUTINES + +These two routines deal with loading `PKGBUILD`(5) files. + + * `unset_PKGBUILD`: + Unsets all variables and functions that might be set in a + `PKGBUILD`(5) file, including those specific to `librefetch`(8). + * `load_PKGBUILD` [<FILE>]: + "Safely" loads a PKGBUILD. Everything that it would normally set + is unset first, $<CARCH> is set according to `makepkg.conf`(5), + then the file is loaded. The file to be loaded is <FILE>, or + "./PKGBUILD" by default. This isn't safe, security wise, in that + the PKGBUILD is free to execute code. + +### SLUGS + +The differences in behavior for anything that takes a slug comes down +to the differences in the output for `list_files` and `list_envvars`. + +The "known" slugs are "abs", "makepkg", "libretools", and anything +beginning with "xbs". If anything else is given, then: + + * `list_files` will give back "/etc/libretools.d/<SLUG>.conf" and + "$<XDG_CONFIG_HOME>/libretools/<SLUG>.conf" + * `list_envvars` will give back an empty list. + +The rules for <SLUG>=(abs|makepkg|libretools) are more easily +expressed in code than prose, so it is recommended that you look at +that. + +## BUGS + +`get_var` and `set_var` do not work with arrays. + +## SEE ALSO + +librelib(7) + +abs.conf(5), makepkg.conf(5), libretools.conf(5), PKGBUILD(5) + +chroot.conf(5), librefetch.conf(5) diff --git a/src/lib/conf.sh.in b/src/lib/conf.sh.in new file mode 100644 index 0000000..8394801 --- /dev/null +++ b/src/lib/conf.sh.in @@ -0,0 +1,243 @@ +#!/hint/bash +# This may be included with or without `set -euE` + +# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +LIBREUSER="${SUDO_USER:-$USER}" +if [[ $LIBREUSER == $USER ]]; then + LIBREHOME=$HOME +else + eval "LIBREHOME=~$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 + +# Low-level 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 + local sysconfdir=${_librelib_conf_sh_sysconfdir:-@sysconfdir@} + local pkgconfdir=${_librelib_conf_sh_pkgconfdir:-@pkgconfdir@} + case $slug in + abs) + echo "${sysconfdir}/$slug.conf" + echo "$LIBREHOME/.$slug.conf" + ;; + makepkg) + local manual="${MAKEPKG_CONF:-}" + local system="${sysconfdir}/$slug.conf" + local olduser="$LIBREHOME/.$slug.conf" + local newuser="$XDG_CONFIG_HOME/pacman/$slug.conf" + if [[ "$manual" != "$system" && -r "$manual" ]]; then + # Manually-specified file + echo "$manual" + else + # Normal file lookup + echo "$system" + if [[ -r "$olduser" && ! -r "$newuser" ]]; then + echo "$olduser" + else + echo "$newuser" + fi + fi + ;; + xbs*) + echo "${sysconfdir}/xbs/$slug.conf" + echo "$XDG_CONFIG_HOME/xbs/$slug.conf" + ;; + libretools) + echo "${sysconfdir}/$slug.conf" + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + *) + echo "${pkgconfdir}/$slug.conf" + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + esac +} + +# Usage: list_envvars $slug +# Lists the environmental variables that take precedence 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 \ + CARCH + ;; + libretools) + printf '%s\n' DIFFPROG + ;; + xbs) + printf '%s\n' BUILDSYSTEM + ;; + *) :;; + esac +} + +# High-level generic functions ################################################# + +# 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 + _l print "Configure '%s' in one of:" "$VAR" + list_files $slug | sed 's/./ -> &/' + else + _l print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)" + fi + ret=1 + fi + done >&2 + + if [[ $ret != 0 ]]; then + return 1 + fi +) + +# Usage: get_var <slug> <var_name> <default_value> +# Does not work with arrays +get_var() ( + set +euE + local slug=$1 + local setting=$2 + local default=$3 + load_files "$slug" + printf '%s' "${!setting:-${default}}" +) + +# Usage: set_var <slug> <var_name> <value> +# Does not work with arrays +set_var() { + local slug=$1 + local key=$2 + local val=$3 + local file + for file in $(list_files "$slug"|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.2.0, dated 2014-12-31 + + # This is kinda weird, but everything is more readable with + # this as a utility function, but I didn't want to introduce a + # new global function, so I just introduced it with the name + # of a function that we get to unset anyway. So it can't + # clash with anything! + mksource() { + # For each arg, `unset -v` all variables matching ${arg} and ${arg}_* + local v + for v in "$@"; do + unset -v "$v" $(declare -p|sed -rn "s/^declare -\S+ (${v}_[a-zA-Z0-9_]*)=.*/\1/p") + done + } + + # This line is taken from the makepkg source + local known_hash_algos=('md5' 'sha1' 'sha224' 'sha256' 'sha384' 'sha512') + + # 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 + + mksource source + unset -v validpgpkeys noextract + local sums=("${known_hash_algos[@]/%/sums}") + mksource "${sums[@]}" + + unset -v groups arch backup + mksource depends makedepends checkdepends optdepends + mksource conflicts provides replaces + unset -v 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 "${sums[@]/#/mk}" + unset -v mkdepends + unset -f mksource +} + +load_PKGBUILD() { + local file=${1:-./PKGBUILD} + unset_PKGBUILD + CARCH="$(get_var makepkg CARCH "`uname -m`")" + . "$file" +} diff --git a/src/lib/libreblacklist b/src/lib/libreblacklist new file mode 100755 index 0000000..6c354fe --- /dev/null +++ b/src/lib/libreblacklist @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +if [[ "${0##*/}" != libreblacklist ]]; then + . "$(librelib blacklist)" +else + set -euE + + lib_file="$(librelib blacklist)" + . "$lib_file" + + usage-outside() { + sed -n '/^# Usage:/,/()/p' "$lib_file" | + 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() { + export TEXTDOMAIN='librelib' + export TEXTDOMAINDIR='/usr/share/locale' + . "$(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 + } + + main() { + if [[ $# -eq 0 ]]; then + usage >&2 + exit 1 + fi + _blacklist_cmd=$1 + shift + if [[ $_blacklist_cmd == -h ]]; then + usage "$@" + else + "blacklist-$_blacklist_cmd" "$@" + fi + } + + main "$@" +fi diff --git a/src/lib/libreblacklist.1.ronn b/src/lib/libreblacklist.1.ronn new file mode 100644 index 0000000..d56eb60 --- /dev/null +++ b/src/lib/libreblacklist.1.ronn @@ -0,0 +1,23 @@ +libreblacklist(1) -- Tools for working with the your-freedom blacklist +====================================================================== + +## SYNOPSIS + +`. "$(librelib blacklist)"`<br> +`. libreblacklist`<br> +`libreblacklist` [-h] <COMMAND> [<ARGS>...] + +## DESCRIPTION + +`libreblacklist` is a set of tools for working with the your-freedom +blacklist. + +It may be included into a `Bash`(1) program as a library, which will +expose it's routines as "blacklist-<COMMAND>", or it may be invoked on +the command line as "libreblacklist <COMMAND>". + +See `libreblacklist -h` for more information. + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib b/src/lib/librelib new file mode 100755 index 0000000..e9d184e --- /dev/null +++ b/src/lib/librelib @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +default_libdir=/usr/lib/libretools + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} + +print() { + local mesg="$(_ "$1")" + shift + printf -- "$mesg\n" "$@" +} + +whitespace_collapse() { + tr '\n' '\r' | sed -r \ + -e 's/\r/ /g' -e 's/\t/ /g' \ + -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' +} + +prose() { + local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u +} + +cmd=${0##*/} +usage() { + . libremessages + print 'Usage: . $(%s LIBRARY)' "$cmd" + print 'Usage: %s -h' "$cmd" + print "Finds a Bash 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)\n' "$cmd" + echo + print "Options:" + flag '-h' 'Show this message' +} + +main() { + if [[ $# != 1 ]]; then + _l usage >&2 + return 2 + fi + if [[ $1 == '-h' ]]; then + _l 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 + _l print '%s: could not find library: %s' "$cmd" "$lib" >&2 + return 1 +} + +main "$@" diff --git a/src/lib/librelib.1.ronn b/src/lib/librelib.1.ronn new file mode 100644 index 0000000..fe64e92 --- /dev/null +++ b/src/lib/librelib.1.ronn @@ -0,0 +1,55 @@ +librelib(1) -- finds a Bash library file +======================================== + +## SYNOPSIS + +`. "$(librelib LIBRARY)"`<br> +`librelib -h` + +## DESCRIPTION + +`librelib` is a program to find a Bash(1) library file at run-time. +This way, the path does not need to be hard-coded into the +application; think of it as a sort of dynamic-linker for shell +programs. + +There are several reasons for doing this, instead of hard-coding the +path: + + * The install path can change in the future without having to change + programs that use them. + * The install directory can be configured at runtime, by setting + `LIBRETOOLS_LIBDIR`, similar to `LD_PRELOAD` (this is used when + running the test suite). + * The naming scheme of a library can change (such as between + `libreNAME` and `NAME.sh` without changing programs that use it. + +By default, `librelib` looks in `/usr/lib/libretools`, but that can be +changed by setting the `LIBRETOOLS_LIBDIR` environmental variable to +the directory it should look in. + +When searching for a library, `librelib` first strips `libre` from the +beginning of the name, and `.sh` from the end. This means that all of +the following are equivalent: + + . "$(librelib messages)" + . "$(librelib messages.sh)" + . "$(librelib libremessages)" + . "$(librelib libremessages.sh)" + +Once it has the 'base' name of the library it is looking for, it looks +for a file with that 'base' name (allowing for, but not requiring +`libre` to be prepended, or `.sh` to be appended) in whichever +directory it is looking in. + +If it cannot find a suitable library file, it will print an error +message to standard error, and exit with a code of 1. + +## Examples + + . "$(librelib messages)" + . "$(librelib conf)" + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib.7.ronn b/src/lib/librelib.7.ronn new file mode 100644 index 0000000..33b0c55 --- /dev/null +++ b/src/lib/librelib.7.ronn @@ -0,0 +1,49 @@ +librelib(7) -- Suite of Bash libraries +====================================== + +## SYNOPSIS + +Overview of the librelib Bash library suite. + +## DESCRIPTION + +There are three parts to librelib: + + 1. The `librelib`(1) executable. + 2. The non-executable libraries installed in `/usr/lib/libretools` + 3. The executable libraries installed in `/usr/bin` + +The `librelib` executable isn't very exciting, it just finds the +libraries installed in `/usr/lib/libretools`. Think of it as a sort +of dynamic-linker. + +The 'core' of librelib are the libraries installed in +`/usr/lib/libretools`. These are `Bash`(1) libraries that may be +sourced in Bash programs. + +Some of these libraries also make sense as stand-alone programs, where +if they are invoked directly, the first argument is the library +routine to be executed. For example, the `messages` library may be +included, or executed: + + . "$(librelib messages)" + msg2 "Foo was found: %s" "$foo" + # or + libremessages msg2 "Foo was found: %s" "$foo" + +The `blacklist` library is similar: + + . "$(librelib blacklist)" + blacklist-update + # or + libreblacklist update + + + +## SEE ALSO + + * librelib(1) + * libremessages(1)/messages.sh(3) + * libreblacklist(1)/blacklist.sh(3) + * conf.sh(3) + * common.sh(3) diff --git a/src/lib/libremessages b/src/lib/libremessages new file mode 100755 index 0000000..647204a --- /dev/null +++ b/src/lib/libremessages @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +if [[ "${0##*/}" != libremessages ]]; then + . "$(librelib messages)" +else + set -euE + . "$(librelib messages)" + "$@" +fi diff --git a/src/lib/libremessages.1.ronn b/src/lib/libremessages.1.ronn new file mode 100644 index 0000000..d4fac85 --- /dev/null +++ b/src/lib/libremessages.1.ronn @@ -0,0 +1,241 @@ +libremessages(1) -- common Bash routines +======================================== + +## SYNOPSIS + +`. "$(librelib messages)"`<br> +`. libremessages`<br> +`libremessages` <COMMAND> + +## DESCRIPTION + +`libremessages` is a shell library containing many common routines. +The name is a bit of a misnomer, it mostly deals with printing +messages, but also has other things. + +`libremessages` uses `common.sh`(3) internally for a large portion of +it's functionality. The authors make no promises that functionality +that is implemented in `libremessages` won't move into `common.sh` or +vice-versa. So, it is recommended that you use `libremessages`, not +`common.sh`. + +### STAND ALONE USAGE + +The "normal" way to use libremessages is to source it, then call the +provided routines. + +However, if you call libremessages directly, the first argument is +taken as the function to call, and the remaining arguments are passed +to it. The only cases where this doesn't work are the lockfile +routines (`lock`, `slock`, and `lock_close`), because lockfiles are +managed as file descriptors. + +### VARIABLES + +The following variables for printing terminal color codes are set: +`ALL_OFF`, `BOLD`, `BLUE`, `GREEN`, `RED`, `YELLOW`. If standard +error is not a terminal (see `isatty`(3)), they are set, but empty. +They are marked as readonly, so it is an error to try to set them +afterwords. + +### MESSAGE FORMAT + +All routines feed the message/format string through `gettext`(1), if +it is available. + +The descriptions will frequently reference `printf`(1)--this isn't +really that `printf`. The program described by the manual page +`printf`(1) is probably the version from GNU coreutils, every time it +is used here, it is `bash`(1)'s internal implementation; try running +the command `help printf` from a Bash shell for more information. + +### GENERAL ROUTINES + +Unless otherwise noted, these do not implicitly call `gettext`. + + * `_` <MESSAGE>: + If `gettext` is available, calls `gettext`, otherwise just prints + the arguments given. + + * `in_array` <NEEDLE> <HAYSTACK>...: + Evaluates whether <HAYSTACK> includes <NEEDLE>; returns 0 if it + does, non-zero if it doesn't. + + * `panic`: + For the times when you can't reasonably continue, similar to + "assert" in some programming languages. + + * `setup_traps` [<HANDLER>]: + Sets traps on TERM, HUP, QUIT and INT signals, as sell as the ERR + event, similar to makepkg. If <HANDLER> is specified, instead of + using the default handler (which is good for most purposes), it + will call <HANDLER> with the arguments + `<HANDLER> <SIGNAL_NAME> <MESSAGE> [<MESSAGE_ARGS>...]`, where + <MESSAGE> is a `printf`(1)-formatted string that is fed through + `gettext`, and <MESSAGE_ARGS> are its arguments. + + * `whitespace_collapse`: + Collapses whitespace on stadard I/O, similar to HTML whitespace + collapsing, with the exception that it puts two spaces between + sentences. It considers newline, tab, and space to be + whitespace. + +### PROSE ROUTINES + +These routines print to standard output, and are useful for printing +word-wrapped prose. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + +To generate gettext `.pot` files for programs using these routines, +plain `xgettext`(1) won't work because it doesn't know about +word-wrapping. Instead, you should use the `librexgettext`(1) program +which knows how do handle word-wrapping, and knows about each of these +routines by default. + + * `prose` <MESSAGE> [<ARGS>...]: + Takes a `printf`(1)-formatted string, collapses whitespace + (HTML-style), and then word-wraps it. + + * `bullet` <MESSAGE> [<ARGS>...]: + Similar to `prose`, but prints a bullet point before the first + line, and indents the remaining lines. + + * `flag` [<FLAG> <DESCRIPTION>|<HEADING>:]...: + Print a flag and description formatted for `--help` text. For + example:<br> + `flag '-N' 'Disable networking in the chroot'`<br> + 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:<br> + `flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'`<br> + Newlines in the description are ignored; it is + whitespace-collapsed (so newlines are stripped), then it is + re-word-wrapped, in the same way as `prose` and `bullet`. If you + pass in multiple flag/description pairs to the same invocation, + the descriptions are all aligned together. The ability to do + insert headings without resetting the alignment is the motivation + for also allowing headings to be in the list. In order to tell + the difference between a flag and a heading, a heading must end + with a colon (':'), and a flag must not. + +### NOTIFICATION ROUTINES + +These routines print to standard error, and all take arguments in the +same format as `printf`(1), except for `stat_done`, which doesn't take +any arguments. Each of these print to stderr, not stdout. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + +To generate gettext `.pot` files for programs using these routines, +plain `xgettext`(1) will work if given correct flags, but you'll find +it easier to use `librexgettext`(1), which will handle each of these +without any extra flags. + + * `plain` <MESSAGE> [<ARGS>...]: + Prints a "plain" message in bold, indented with 4 spaces. + + * `msg` <MESSAGE> [<ARGS>...]: + Prints a top-level priority notification. + + * `msg2` <MESSAGE> [<ARGS>...]: + Prints a secondary notification. + + * `warning` <MESSAGE> [<ARGS>...]: + Prints a warning. + + * `error` <MESSAGE> [<ARGS>...]: + Prints an error message. + + * `stat_busy` <MESSAGE> [<ARGS>...]: + Prints a "working..." type message without a trailing newline. + + * `stat_done`: + Prints a "done" type message to terminate `stat_busy`. + + * `print` <MESSAGE> [<ARGS>...]: + Like `printf`(1), but `gettext`-aware, and automatically prints a + trailing newline. + + * `term_title` <MESSAGE> [<ARGS>...]: + Sets the terminal title to the specified message. + +### TEMPORARY DIRECTORY MANAGEMENT + +These are used by devtools, and not used within the rest of +libretools. + +They work by creating and removing the directory referred to by the +variable $<WORKDIR>; `libretools.conf`(5) uses the same variable to +where the user saves all their work. If you aren't careful with +these, you could end up deleting a lot of someone's work. + + * `setup_workdir`: + Creates a temporary directory, and sets the environmental + variable $<WORKDIR> to it. Also sets traps for the signals INT, + QUIT, TERM and HUP to run `abort`; and EXIT to run `cleanup` + (see `signal`(7)). + + * `cleanup` [<EXIT_STATUS>]: + *If* `setup_workdir` has been run, `rm -rf "$WORKDIR"`. If given + a numeric argument, it will then call `exit`(1) with that + argument, otherwise it calls `exit`(1) with a status of 0. + + * `abort`: + Calls `msg` with the message "Aborting...", then calls + `cleanup 255`. + + * `die` <MESSAGE> [<ARGS>...]: + Exactly like `error`, but calls `cleanup` and calls `exit`(1) + with a status of 255. + +### LOCKFILE ROUTINES + + * `lock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Opens (creating if necessary) the file <LOCKFILE> with file + descriptor <FD> in the current process, and gets an exclusive + lock on it. If another program already has a lock on the file, + and this program needs to wait for the lock to be released, then + it uses `stat_busy`/`stat_done` to print <MESSAGE>. + + * `slock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Identical like `lock`, but opens a shared lock. This is also + known as a "read lock". Many programs can have a shared lock at + the same time, as long as no one has an exclusive lock on it. + + * `lock_close` <FD>: + Closes file descriptor <FD>, releasing the lock opened on it. + +### MAKEPKG ROUTINES + +These routines relate to `makepkg`(8). + + * `find_cached_package` <PKGNAME> <PKGVER>[-<PKGREL] <ARCH>: + Searches for a locally built copy of the specified package, in + <PKGDEST> and the current working directory. If <PKGREL> is not + specified, any value will match. If multiple matching files are + found (not counting duplicate links), then an error is printed to + stderr and nothing is printed to stdout. + + * `get_full_version` [<PKGNAME>]: + Inspects variables that are set, and prints the full version + spec, including <epoch> if necessary, <pkgver>, and <pkgrel>. By + default, it will print the information for <pkgbase>, following + the normal rules for finding <pkgbase>. If <PKGNAME> is given, + it will print the information for that sub-package. The versions + for different parts of a split package don't have to be the same! + +## BUGS + +`term_title` currently only knows about the terminals screen, tmux, +xterm and rxvt (and their various <TERM> values; +"rxvt-unicode-256color" is still rxvt). + +## SEE ALSO + +librexgettext(1), librelib(7), gettext(1), common.sh(3) + +Things that were mentioned: + +bash(1), xgettext(1), exit(1), isatty(3), libretools.conf(5), +makepkg(8), printf(1), signal(7) diff --git a/src/lib/librexgettext b/src/lib/librexgettext new file mode 100755 index 0000000..db575d6 --- /dev/null +++ b/src/lib/librexgettext @@ -0,0 +1,226 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +export TEXTDOMAIN='librelib' +export TEXTDOMAINDIR='/usr/share/locale' + +default_simple=( + --keyword={eval_,}{gettext,'ngettext:1,2'} + --keyword={_,print,term_title} + --keyword={msg,msg2,warning,error,stat_busy,die} + --keyword={lock,slock}:3 +) +default_prose=(--keyword={prose,bullet}) + +readonly default_simple default_prose + +if ! type gettext &>/dev/null; then + gettext() { echo "$@"; } +fi + +errusage() { + if [[ $# -gt 0 ]]; then + fmt="$(gettext "$1")"; shift + printf "${0##*/}: $fmt\n" "$@" + fi + usage >&2 +} + +usage() { + . libremessages + print 'Usage: %s [OPTIONS] FILES...' "${0##*/}" + print 'Generates .pot files for programs using libremessages' + echo + prose 'In librexgettext, there are 2 types of keywords:' + bullet 'simple: Simple keywords are just like normal xgettext' + bullet 'prose: Prose keywords are similar, but the text is + word-wrapped' + prose 'The keyword format is the same as in GNU xgettext.' + echo + prose 'The libremessages `flag` command is also handled + specially, and is not configurable as a keyword.' + echo + prose 'The default simple keywords are: %s' "${default_simple[*]#--keyword=}" + echo + prose 'The default prose keywords are: %s' "${default_prose[*]#--keyword=}" + echo + print 'Options:' + flag \ + '--simple=KEYWORD' 'Look for KEYWORD as an additional simple keyword' \ + '--prose=KEYWORD' 'Look for KEYWORD as an additional prose keyword' \ + '-k' 'Disable using the default keywords' \ + '-h, --help' 'Show this text' +} + +xgettext-sh() { + xgettext --omit-header --from-code=UTF-8 -L shell -k -o - "$@" +} + +xgettext-flag() { + { + # Stage 1: Generate + # + # Get all of the arguments to `flag`. Because `flag` + # takes an arbitrary number of arguments, just iterate + # through arg1, arg2, ... argN; until we've come up + # empty 3 times. Why 3? Because each flag takes 2 + # arguments, and because we don't keep track of which + # one of those we're on, waiting for 3 empties ensures + # us that we've had a complete "round" with nothing. + # + # Why can't I just do i+=2, and not have to keep track + # of empties? Because, we also allow for arguments + # ending in a colon to be headings, which changes the + # offsets. + declare -i empties=0 + declare -i i + for (( i=1; empties < 3; i++ )); do + local out + out="$(xgettext-sh --keyword="flag:$i,\"$i\"" "$@")" + if [[ -n $out ]]; then + printf -- '%s\n' "$out" + else + empties+=1 + fi + done + } | whitespace-collapse | sed '/^\#, sh-format/d' | { + # Stage 2: Parse + # + # Read in the lines, and group them into an array of + # (multi-line) msgs. This just makes working with + # them easier. + local msgs=() + declare -i i=-1 + local re='^#\. ([0-9]+)$' + IFS='' + local line + while read -r line; do + if [[ $line =~ $re ]]; then + i+=1 + fi + msgs[$i]+="$line"$'\n' + done + # Stage 3: Sort + # + # Now, we have the `msgs` array, and it is + # sorted such that it is all of the arg1's to `flag`, + # then all of the arg2's, then all of the arg3's, and + # so on. We want to re-order them such that it's all + # of the args for the first invocation then all of the + # args for the second; and so on. + # + # We do this by simply sorting them by the location + # that they appear in the file. Then, when we see the + # argument number go back down to 1, we know that a + # new invocation has started! + IFS=$'\n' + local locations=($( + local i + for i in "${!msgs[@]}"; do + declare -i arg row + local lines=(${msgs[$i]}) + arg=${lines[0]#'#. '} + row=${lines[1]##*:} + printf '%d.%d %d\n' "$row" "$arg" "$i" + done | sort -n + )) + # Stage 4: Output + # + # Now, we prune out the arguments that aren't + # localizable. Also, remove the "#." comment lines. + # As explained above (in stage 3), when we see $arg go + # to 1, that's the beginning of a new invocation. + local expectflag=true + local location + for location in "${locations[@]}"; do + IFS=' .' + local row arg i + read -r row arg i <<<"$location" + local msg="${msgs[$i]#*$'\n'}" + # Now we operate based on $row, $arg, and $msg + if [[ $arg == 1 ]]; then + expectflag=true + fi + if $expectflag; then + IFS=$'\n' + local lines=(${msg}) + if [[ ${lines[1]} == *':"' ]]; then + # We expected a flag, but got + # a heading + printf -- '%s\n' "$msg" + else + # We expected a flag, and got + # one! + expectflag=false + fi + else + printf -- '%s\n' "$msg" + expectflag=true + fi + done + } +} + +whitespace-collapse() { + tr '\n' '\r' | sed 's/"\r\s*"//g' | tr '\r' '\n' | # This removes the awkward word-wrapping done by xgettext + sed -r -e 's/(\\n|\\t|\t)/ /g' -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' # This collapses whitespace +} + +main() { + local simple=() + local prose=() + local files=() + local use_defaults=true + local error=false + + declare -i i + for (( i=1; i <= $#; i++ )); do + case "${!i}" in + --simple) i+=1; simple+=(--keyword="${!i}");; + --simple=*) simple+=(--keyword="${!i#*=}");; + --prose) i+=1; prose+=(--keyword="${!i}");; + --prose=*) prose+=(--keyword="${!i#*=}");; + -k) use_defaults=false;; + --help|-h) usage; return 0;; + --) i+=1; break;; + -*) errusage "unrecognized option: %s" "${!i}"; error=true;; + *) files+=("${!i}");; + esac + done + files+=("${@:$i}") + if [[ ${#files[@]} -lt 1 ]]; then + errusage "no input file given" + error=true + fi + if "$error"; then + return 1 + fi + if "$use_defaults"; then + simple+=("${default_simple[@]}") + prose+=("${default_prose[@]}") + fi + + # Main code + { + xgettext-sh "${simple[@]}" -- "${files[@]}" + xgettext-sh "${prose[@]}" -- "${files[@]}" | whitespace-collapse + xgettext-flag -- "${files[@]}" + } | sed '/^\#, sh-format/d' | msguniq -Fi --to-code=UTF-8 +} + +main "$@" diff --git a/src/lib/librexgettext.1.ronn b/src/lib/librexgettext.1.ronn new file mode 100644 index 0000000..de6abde --- /dev/null +++ b/src/lib/librexgettext.1.ronn @@ -0,0 +1,18 @@ +librexgettext(1) -- Extrct libremessages gettext strings from sources +===================================================================== + +## SYNOPSIS + +`librexgettext` [OPTIONS] FILES<br> +`librexgettext` [-h|--help] + +## DESCRIPTION + +`librexgettext` is a tool for extracting gettext strings from programs +using the fancy word-wrapping utilities of `libremessages`(1). + +See `librexgettext --help` for more information. + +## SEE ALSO + +libremessages(1) diff --git a/src/lib/messages.sh b/src/lib/messages.sh new file mode 100644 index 0000000..0125003 --- /dev/null +++ b/src/lib/messages.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` + +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the setup_traps() function: +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# +# License: GNU GPLv2+ +# +# 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/>. + +################################################################################ +# Inherit most functions from devtools # +################################################################################ + +. "$(librelib common.sh)" + +################################################################################ +# Own functions # +################################################################################ + +# Usage: panic +# +# For programming errors, bails immediately with little fanfare. +panic() { + echo "$(_l _ 'panic: malformed call to internal function')" >&2 + exit 1 +} + +# Usage: print MESG [ARGS...] +# +# Like printf, but gettext-aware, and prints a trailing newline +print() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$1")" + shift + printf -- "$mesg\n" "$@" +} + +# Usage: whitespace_collapse <<<STRING +# +# Collapses whitespace on stadard I/O, similar to HTML whitespace +# collapsing, with the exception that it puts two spaces between +# sentences. It considers newline, tab, and space to be whitespace. +whitespace_collapse() { + [[ $# == 0 ]] || panic + + tr '\n' '\r' | sed -r \ + -e 's/\r/ /g' -e 's/\t/ /g' \ + -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' +} + + +# Usage: prose MESG [ARGS...] +# +# 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="$(_ "$(whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u +} + +# Usage: bullet MESG [ARGS...] +# Like prose, but print a bullet "-" before the first line, and indent the +# remaining lines. +bullet() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(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|HEADING:]... +# +# Print a flag and description formatted for --help text. +# +# ex: flag '-C <FILE>' 'Use this file instead of pacman.conf' +# +# The descriptions and headings are fed through gettext, the flags ar +# not, so if part of a 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. If you pass in multiple flag/description pairs at once, +# the descriptions are all alligned together. +# +# A heading MUST end with a colon (':'), this is how it knows that it +# is a heading. Similarly, a flag MUST NOT end with a colon. +flag() { + local args=("$@") + + declare -i flaglen=0 + while [[ $# -gt 0 ]]; do + if [[ $1 == *: ]]; then + shift 1 + else + if [[ ${#1} -gt $flaglen ]]; then + flaglen=${#1} + fi + shift 2 + fi + done + set -- "${args[@]}" + + # Unless the $flaglen is extra-wide, the $desc should start at + # column 16 (that is two literal-tabs). If $flaglen is wide, + # this should be increased in increments of 8 (that is, a + # literal-tab). Everything should be wrapped to 75 columns. + + # The printf-format we use has 4 spaces built into it (two at + # the beginning, and two for a seperator). Therefore, the + # default indent is 16-4=12 columns. And the width left for + # $desc is (75-4)-indent = 71-indent. + + declare -i indent=12 + while [[ $indent -lt $flaglen ]]; do + indent+=8 + done + local fmt2 fmt1 + fmt2=" %-${indent}s %s\n" + printf -v fmt1 " %-${indent}s %%s\n" '' + + while [[ $# -gt 0 ]]; do + if [[ $1 == *: ]]; then + printf -- ' %s\n' "$(_ "$1")" + shift + else + [[ $# -gt 1 ]] || panic + local flag=$1 + local desc="$(_ "$(whitespace_collapse <<<"$2")")" + shift 2 + + local lines + IFS=$'\n' lines=($(fmt -u -w $((71-indent)) <<<"$desc")) + printf -- "$fmt2" "$flag" "${lines[0]}" + [[ ${#lines[@]} -lt 2 ]] || printf -- "$fmt1" "${lines[@]:1}" + fi + done +} + +# Usage: term_title MESG [ARGS...] +# +# 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" "$(printf -- "$@")" +} + +# Usage: setup_traps [handler] +# +# Sets up traps on TERM, HUP, QUIT and INT signals, as well as the ERR +# event, similar to makepkg. +# +# If `handler` is specified, instead of using the default handler +# (which is good for most purposes), it will call the command handler +# with the arguments: +# +# ${handler} SIGNAL_NAME MESSAGE_FMT [MESSAGE_PARAMS...] +# +# where MESSAGE_* are printf-like stuff. +# +# This function is based on code from pacman:makepkg +setup_traps() { + [[ $# -le 1 ]] || panic + if [[ $# == 1 ]]; then + eval "_libremessages_trap_exit() { $1 \"\$@\"; }" + else + _libremessages_trap_exit() { + local signal=$1; shift + echo + error "$@" + trap -- "$signal" + kill "-$signal" "$$" + } + fi + set -E + for signal in TERM HUP QUIT; do + trap "_libremessages_trap_exit $signal '%s signal caught. Exiting...' $signal" $signal + done + trap '_libremessages_trap_exit INT "Aborted by user! Exiting..."' INT + trap '_libremessages_trap_exit USR1 "An unknown error has occurred. Exiting..."' ERR +} diff --git a/src/lib/messages.sh.3.ronn b/src/lib/messages.sh.3.ronn new file mode 120000 index 0000000..391ecbd --- /dev/null +++ b/src/lib/messages.sh.3.ronn @@ -0,0 +1 @@ +libremessages.1.ronn
\ No newline at end of file diff --git a/src/librefetch/.gitignore b/src/librefetch/.gitignore new file mode 100644 index 0000000..d34a2fc --- /dev/null +++ b/src/librefetch/.gitignore @@ -0,0 +1,2 @@ +librefetch-install +librefetch-makepkg.conf diff --git a/src/librefetch/Makefile b/src/librefetch/Makefile new file mode 100644 index 0000000..26ee2ee --- /dev/null +++ b/src/librefetch/Makefile @@ -0,0 +1,13 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-bins = librefetch librefetch-install +libretools-confs += librefetch-makepkg.conf +libretools-libexecs += $(filter librefetchdir/%,$(detect-exec)) +libretools-libs += $(filter-out $(libretools-libexecs),$(filter librefetchdir/%,$(detect-all))) +pots = $(libretools-bins) + +$(outdir)/librefetch-install: $(var)pkgconfdir +$(outdir)/librefetch-makepkg.conf: $(var)bindir + +include $(topsrcdir)/automake.tail.mk diff --git a/src/librefetch/librefetch b/src/librefetch/librefetch new file mode 100755 index 0000000..2b8af61 --- /dev/null +++ b/src/librefetch/librefetch @@ -0,0 +1,400 @@ +#!/usr/bin/env bash +# librefetch +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the create_signature() function: +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# create_signature() is taken from pacman:makepkg, which is GPLv2+, +# so we take the '+' to combine it with our GPLv3+. + +. "$(librelib conf)" +. "$(librelib messages)" +setup_traps + +tmpfiles=() +tmpdirs=() +trap 'rm -f -- "${tmpfiles[@]}"; rm -rf -- "${tmpdirs[@]}"' EXIT + +cmd=${0##*/} +usage() { + print "Usage: %s [OPTIONS] SOURCE_URL [OUTPUT_FILE]" "$cmd" + print "Usage: %s -[g|S|M|h]" "$cmd" + print "Downloads or creates a liberated source tarball." + echo + prose "The default mode is to create OUTPUT_FILE, first by trying + download mode, then create mode." + echo + prose "If OUTPUT_FILE isn't specified, it defaults to the non-directory + part of SOURCE_URL, in the current directory." + echo + prose "Unless '-C' is specified, if SOURCE_URL does not begin with a + configured mirror, create mode is inhibited." + echo + prose "In download mode, it simply tries to download SOURCE_URL. At the + beginning of a URL, 'libre://' expands to the first configured + mirror." + echo + prose "In create mode, it either looks at a build script and uses that + to create the source tarball, or it uses GPG to create a + signature (if OUTPUT_FILE ends with \`.sig\` or \`.sig.part\`). + If it is using GPG to create a signature, but the file that it is + trying to sign doesn't exist yet, it recurses on itself to first + create that file. SOURCE_URL is ignored, except that it is used + to set the default value of OUTPUT_FILE, and that it may be used + when recursing." + echo + prose "The default build script is 'PKGBUILD', or 'SRCBUILD' if it + exists." + echo + prose "Other options, if they are valid \`makepkg\` options, are passed + straight to makepkg." + echo + print "Example usage:" + print ' $ %s https://repo.parabola.nu/other/mypackage/mypackage-1.0.tar.gz' "$cmd" + echo + print "Options:" + flag 'Settings:' \ + "-C" "Force create mode (don't download)" \ + "-D" "Force download mode (don't create)" \ + "-p <$(_ FILE)>" "Use an alternate build script (instead of + 'PKGBUILD'). If an SRCBUILD exists in the same + directory, it is used instead" \ + 'Alternate modes:' \ + "-g, --geninteg" "Generate integrity checks for source files" \ + "-S, --srcbuild" "Print the effective build script (SRCBUILD)" \ + "-M, --makepkg" "Generate and print the location of the + effective makepkg script" \ + "-h, --help" "Show this message" +} + +main() { + BUILDFILE="$(realpath -Lm PKGBUILD)" + makepkg_opts=() + extra_opts=() + mode=download-create + if ! parse_options "$@"; then + usage >&2 + exit 1 + fi + + doit +} + +doit() { + # Mode: help ########################################################### + + if [[ $mode =~ help ]]; then + usage + exit 0 + fi + + ######################################################################## + + makepkg="$(modified_makepkg)" + + # Mode: makepkg ######################################################## + + if [[ $mode =~ makepkg ]]; then + printf '%s\n' "$makepkg" + exit 0 + else + tmpdirs+=("${makepkg%/*}") + fi + + ######################################################################## + + local BUILDFILEDIR="${BUILDFILE%/*}" + if [[ -f "${BUILDFILEDIR}/SRCBUILD" ]]; then + BUILDFILE="${BUILDFILEDIR}/SRCBUILD" + fi + if [[ ! -f "$BUILDFILE" ]]; then + error "%s does not exist." "$BUILDFILE" + exit 1 + fi + case "$BUILDFILE" in + */SRCBUILD) srcbuild="$(modified_srcbuild "$BUILDFILE")";; + *) srcbuild="$(modified_pkgbuild "$BUILDFILE")";; + esac + tmpfiles+=("$srcbuild") + + # Mode: checksums ###################################################### + + if [[ $mode =~ checksums ]]; then + "$makepkg" "${makepkg_opts[@]}" -g -p "$srcbuild" | + case ${BUILDFILE##*/} in + PKGBUILD) sed -e 's/^[a-z]/mk&/' -e 's/^\s/ &/';; + SRCBUILD) cat;; + esac + exit 0 + fi + + # Mode: srcbuild ####################################################### + + if [[ $mode =~ srcbuild ]]; then + cat "$srcbuild" + exit 0 + fi + + ######################################################################## + + local src="${extra_opts[0]}" + local dst="${extra_opts[1]:-${src##*/}}" + + # Don't canonicalize $src unless mode =~ download, and we've validated + # that $MIRRORS is configured. + + # Canonicalize $dst + dst="$(realpath -Lm -- "$dst")" + + # Mode: download ####################################################### + + if [[ $mode =~ download ]]; then + load_files librefetch + check_vars librefetch MIRRORS DOWNLOADER || exit 1 + + # Canonicalize $src + if [[ "$src" == libre://* ]]; then + src="${MIRRORS[0]}/${src#libre://}" + fi + + # check to see if $src is a candidate for create mode + local inmirror=false; + local mirror + for mirror in "${MIRRORS[@]}"; do + if [[ "$src" == "$mirror"* ]]; then + inmirror=true + break + fi + done + if ! $inmirror; then + # inhibit create + mode=download + fi + + local dlcmd="${DOWNLOADER}" + [[ $dlcmd = *%u* ]] || dlcmd="$dlcmd %u" + dlcmd="${dlcmd//\%o/\"\$dst\"}" + dlcmd="${dlcmd//\%u/\"\$src\"}" + + { eval "$dlcmd"; } >&2 && exit 0 + fi + + # Mode: create ######################################################### + + if [[ $mode =~ create ]]; then + local base_dst=${dst%.part} + local suffix=${dst#"$base_dst"} + + if [[ $base_dst == *.sig ]]; then + if ! [[ -e $base_dst ]]; then + extra_opts=("${src%.sig}" "${base_dst%.sig}") + doit || exit $? + fi + create_signature "${base_dst%.sig}" || exit $? + if [[ -n $suffix ]]; then + mv -f "$base_dst" "$dst" + fi + else + export PKGEXT=${base_dst##*/} + export PKGDEST=${dst%/*} + export pkg_file=$dst + + cd "$BUILDFILEDIR" + "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >&2 || exit $? + fi + fi +} + +# sets the variables BUILDFILE, makepkg_opts, extra_opts, mode +parse_options() { + declare -i ret=0 + local {shrt,long}{1,2} + + # makepkg options + local makepkg_orig="$(which makepkg)" + shrt1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.)(,| [^<]).*/\1/p')) + shrt2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.) <.*/\1/p')) + long1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn -e 's/^ +(-., )?--(\S*) [^<].*/\2/p')) + long2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +--(\S*) <.*/\1/p')) + + # librefetch options + shrt1+=(C D g S M h) + shrt2+=(p) + long1+=(geninteg srcbuild makepkg help) + long2+=() + + # Feed the options through getopt (sanitize them) + local shrt="$({ printf '%s\0' "${shrt1[@]}"; printf '%s:\0' "${shrt2[@]}"; } | sort -zu | xargs -0 printf '%s')" + local long="$({ printf '%s\0' "${long1[@]}"; printf '%s:\0' "${long2[@]}"; } | sort -zu | xargs -0 printf '%s,')" + local args="$(getopt -n "$cmd" -o "$shrt" -l "${long%,}" -- "$@")" + ret=$? + eval set -- "$args" + unset shrt long args + + # Parse the options. + local opt optarg have_optarg + while [[ $# -gt 0 ]]; do + opt=$1; shift + have_optarg=false + + if { [[ $opt == --?* ]] && in_array "${opt#--}" "${long2[@]}"; } \ + || { [[ $opt == -? ]] && in_array "${opt#-}" "${shrt2[@]}"; } + then + optarg=$1; shift + have_optarg=true + fi + + case "$opt" in + -C) mode=create;; + -D) mode=download;; + -g|--geninteg) mode=checksums;; + -S|--srcbuild) mode=srcbuild;; + -M|--makepkg) mode=makepkg;; + -p) BUILDFILE="$(realpath -Lm -- "$optarg")";; + -h|--help) mode=help;; + --) break;; + *) + makepkg_opts+=("$opt") + if $have_optarg; then makepkg_opts+=("$optarg"); fi + ;; + esac + done + extra_opts+=("$@") + + # check the number of extra_opts + case "$mode" in + help) # don't worry about it + :;; + checksums|srcbuild|makepkg) # don't take any extra arguments + if [[ ${#extra_opts[@]} != 0 ]]; then + print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >&2 + ret=1 + fi + ;; + *download*|*create*) # take 1 or 2 extra arguments + if [[ ${#extra_opts[@]} != 1 ]] && [[ ${#extra_opts[@]} != 2 ]]; then + print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >&2 + ret=1 + fi + ;; + esac + + return $ret +} + +# Modify makepkg ############################################################### + +modified_makepkg() { + local dir="$(mktemp --tmpdir --directory "${cmd}.XXXXXXXXXXX.makepkg")" + make -s -f "$(librelib librefetchdir/Makefile)" new="$dir" + realpath -es "$dir/makepkg" +} + +# 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[@]}") ; unset "source_${CARCH}" +noextract=("${mknoextract[@]}") + +declare algo +for algo in "${known_hash_algos[@]}"; do + eval "${algo}sums=(\"\${mk${algo}sums[@]}\")" + unset "${algo}sums_${CARCH}" +done + +depends=() ; unset "depends_${CARCH}" +checkdepends=() ; unset "checkdepends_${CARCH}" +makedepends=("${mkdepends[@]}") ; unset "makedepends_${CARCH}" + +#### +options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx !optipng !debug) +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="$(mktemp "${pkgbuild%/*}/${cmd}.XXXXXXXXXXX.PKGBUILD.to.SRCBUILD")" + printf '%s' "$pkgbuild_append" | cat "$pkgbuild" - > "$srcbuild" + printf '%s\n' "$srcbuild" +} + + +# Modify SRCBUILD ############################################################## + +modified_srcbuild() { + local orig=$1 + local srcbuild="$(mktemp "${orig%/*}/${cmd}.XXXXXXXXXXX.SRCBUILD.to.SRCBUILD")" + sed -e '/PKGDEST=/d' -e '/PKGEXT=/d' < "$orig" > "$new" + printf '%s\n' "$new" +} + +################################################################################ + +# This function is taken almost verbatim from makepkg +create_signature() { + local ret=0 + local filename="$1" + msg "Signing package..." + + local SIGNWITHKEY=() + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY=(-u "${GPGKEY}") + fi + + gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" --no-armor "$filename" &>/dev/null || ret=$? + + + if (( ! ret )); then + msg2 "Created signature file %s." "$filename.sig" + else + error "Failed to sign package file." + return $ret + fi +} + +main "$@" diff --git a/src/librefetch/librefetch-install.in b/src/librefetch/librefetch-install.in new file mode 100644 index 0000000..34815a7 --- /dev/null +++ b/src/librefetch/librefetch-install.in @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# lirefetch-install: (un)install librefetch to /etc/makepkg.conf +# +# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +set -ueE +. "$(librelib messages)" + +# This line should be installed +new_code='. @pkgconfdir@/librefetch-makepkg.conf # This line was added by librefetch-install' + +# These lines were installed by previous versions of this script +old_code=( + '# The following line is added by the libretools post_install script' + '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=("libre::/usr/bin/librefetch -p \"\$BUILDFILE\" %u %o")' + '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p \"\$BUILDFILE\" -- %u %o")' + 'DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p $(printf "%q" "$BUILDFILE") -- %u %o")' + 'DLAGENTS+=({https,libre}'\''::/usr/bin/librefetch -p "$BUILDFILE" -- %u %o'\'')' +) + +# has_line $file $line +has_line() { + local file=$1 + local line=$2 + grep -Fxq -- "$line" "$file" +} +# del_line $file $line +del_line() { + local file=$1 + local line=$2 + local lineno=($(grep -Fxn -- "$line" "$file" | cut -d: -f1)) + if [[ "${#lineno[@]}" -gt 0 ]]; then + sed -i "$(printf '%dd;' "${lineno[@]}")" "$file" + fi +} + +# post_install $file +post_install() { + local file=$1 + if grep -q 'librefetch' "$file"; then + msg2 "%s: librefetch is already in %q" "${0##*/}" "$(realpath -s "$file")" + local line del=false + for line in "${old_code[@]}"; do + if has_line "$file" "$line"; then + msg2 "%s: ... but it's an old version" "${0##*/}" + pre_remove "$file" + post_install "$file" + return $? + fi + done + else + msg2 "%s: adding librefetch to %q" "${0##*/}" "$(realpath -s "$file")" + printf '%s\n' "$new_code" >> "$file" + fi +} + +# pre_remove $file +pre_remove() { + local file=$1 + msg2 "%s: removing librefetch from %q" "${0##*/}" "$(realpath -s "$file")" + + # Check for the old stuff + sed -ri 's/^#(.*) # commented out by the libretools post_install script/\1/' "$file" + local line + for line in "${old_code[@]}"; do + del_line "$file" "$line" + done + + # Check for the current stuff + del_line "$file" "$new_code" +} + +usage() { + print "Usage: %s [install|remove] MAKEPKG_CONF_FILE" "${0##*/}" + print "Adds or removes librefetch to/from makepkg.conf:DLAGENTS" +} + +main() { + if [[ $# != 2 ]]; then + usage >&2 + return 1 + fi + local file=$2 + if [[ ! -f "$file" ]]; then + msg2 "%s: does not exist: %q" "${0##*/}" "$(realpath -s "$file")" + fi + if [[ ! -w "$file" ]]; then + msg2 "%s: cannot write to file: %q" "${0##*/}" "$(realpath -s "$file")" + fi + case "$1" in + install) post_install "$file";; + remove) pre_remove "$file";; + *) usage >&2; return 1;; + esac +} + +main "$@" diff --git a/src/librefetch/librefetch-makepkg.conf.in b/src/librefetch/librefetch-makepkg.conf.in new file mode 100644 index 0000000..723bc15 --- /dev/null +++ b/src/librefetch/librefetch-makepkg.conf.in @@ -0,0 +1,16 @@ +#!/hint/bash + +# This file should be 'source'd by makepkg.conf to enable librefetch +edit_dlagents() { + local dlagent="@bindir@/librefetch -p $(printf %q "$(realpath -Lm "${BUILDFILE:-${BUILDSCRIPT:-PKGBUILD}}")") -- %u %o" + + local i + for i in "${!DLAGENTS[@]}"; do + if [[ "${DLAGENTS[$i]}" = https::* ]]; then + DLAGENTS[$i]="https::$dlagent" + fi + done + DLAGENTS+=("libre::$dlagent") +} +edit_dlagents +unset -f edit_dlagents diff --git a/src/librefetch/librefetch.8.ronn b/src/librefetch/librefetch.8.ronn new file mode 100644 index 0000000..cf009de --- /dev/null +++ b/src/librefetch/librefetch.8.ronn @@ -0,0 +1,214 @@ +librefetch(8) -- downloads or creates a liberated source tarball +================================================================ + +## SYNOPSIS + +`librefetch` [<OPTIONS>] <SOURCE-URL> [<OUTPUT-FILE>]<br> +`librefetch` `-`[`g`|`S`|`M`|`h`] + +## DESCRIPTION + +`librefetch` is a program to streamline creation of custom source +tarballs for `PKGBUILD(5)` files. + +If a URL mentioned in the <source> array in a `PKGBUILD` is in a +location that Parabola uploads "custom" source tarballs to (or +configured locations), and no file is at that URL, librefetch will +automatically create it for you. + +This works because a post-install script for the package configures +`librefetch` as the download agent for `https://` URLs in +`makepkg.conf`; allowing it to jump in and create a file if need be. +Because of this, it is almost never necessary to call `librefetch` +manually. + +The post-install script also configures `librefetch` as the download +agent for `libre://` URLs, for compatibility with `PKGBUILD` files +that used a previous version of librefetch. + +There are 5 modes: + + * `download`: Download the tarball from the configured mirror. + * `create`: Create the tarball from a `PKGBUILD`/`SRCBUILD`. + * `checksums`: Generate integrity checks for source files. + * `srcbuild`: Print the effective build script. + * `makepkg`: Generate and print the location of the effective makepkg + script. + * `help`: Print `librefetch` usage information. + +The normal mode of operation is `download` mode. If `download` mode +fails, it may choose to try `create` mode. + +## 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. + * `-S` | `--srcbuild`: Use `srcbuild` mode: print the effective build + script. + * `-M` | `--makepkg`: Use `makepkg` mode: generate and print the + location of the effective makepkg script. + * `-h` | `--help`: Use `help` mode: Show usage information. + +Other options, if they are documented in `makepkg -h`, are passed to +the modified copy of makepkg created during `create` mode. + +## DOWNLOAD MODE + +If <SOURCE-URL> begins with the string `libre://`, it is replaced with +the first value in <MIRRORS>, as configured in `librefetch.conf(5)`; +this is for compatibility with `PKGBUILD` files that used a previous +version of librefetch. + +It uses <DOWNLOADER>, as configured in `librefetch.conf` to attempt to +download the source tarball from that URL. If that fails, and +following conditions are met, it proceeds to `create` mode: + + * The `-D` flag has not been specified to inhibit `create` mode. + * The <SOURCE-URL> begins with one of the values in <MIRRORS>. + +The latter requirement allows librefetch to be used as a generic +HTTP(S) download agent, that can automatically create files from +whitelisted locations. + +## CREATE MODE + +The principle of `create` mode is that a special `PKGBUILD(5)` (called +`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. +If the output filename does not end with `.sig` or `.sig.part`, then +the `SRCBUILD` is fed to a modified version of `makepkg(8)`. If the +output filename does end with `.sig` or `.sig.part`, then it uses GPG +to create a signature. If the file it is trying to sign does not +exist yet, librefetch recurses on itself to create it. + +The reason `makepkg` must be modified is that we need the resulting +tarball to be deterministic (as well as not containing package +metadata). + +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 `srcbuild` 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 temporary 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, sha224, sha256, sha384, sha512) + depends = <empty> + checkdepends = <empty> + makedepends = mkdepends + *_$CARCH = <unset> + 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=()>, respectively. + * Declare a `mksource()` function to make modifications to the + source, if necessary. + +Other changes: + + * <pkgname> is set to <pkgbase>, or the first element of the + <pkgname> array (the effect is that split packaging is turned + off). + * <options=()> is set have `makepkg` avoid making changes to + <$pkgdir>. The exact change is: + + options=(!strip docs libtool staticlibs 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 the date specified in + <SOURCE_DATE_EPOCH>[1], or "1990-01-01 0:0:0 +0" if it's not set, so + that the resulting tarball will be the same, regardless of when it + was created. + * Sort the files included in the tarball; normally the order of files + in a tarball is essentially random (even if it tends to be the same + when re-created on the same machine). + * append `-libre` to <$srcdir> + * append `-libre` to <$pkgbasedir> (which becomes <$pkgdir>) + * Don't check if the package has already been built. + +For debugging purposes, this modified makepkg can be printed instead +of executed with `makepkg` mode. Before it is run in create mode, +`PKGEXT`, `PKGDEST`, and `pkg_file` are set as environment variables. + +## 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) + +## NOTES + + 1. <SOURCE_DATE_EPOCH> specification for build systems + https://reproducible-builds.org/specs/source-date-epoch/ diff --git a/src/librefetch/librefetch.conf b/src/librefetch/librefetch.conf new file mode 100644 index 0000000..7251ff3 --- /dev/null +++ b/src/librefetch/librefetch.conf @@ -0,0 +1,7 @@ +MIRRORS=( + 'https://repo.parabola.nu/sources/' + 'https://repo.parabola.nu/other/' + 'https://repo.parabolagnulinux.org/sources/' + 'https://repo.parabolagnulinux.org/other/' +) +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..29df4d2 --- /dev/null +++ b/src/librefetch/librefetch.conf.5.ronn @@ -0,0 +1,42 @@ +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 precedence 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 + + * `MIRRORS=( ... )`: + A list of locations that generated source tarballs may be located + at. If a URL begins with `libre://`, then `libre://` is replaced + with the first location listed here. + * `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. The format and semantics are similar to + `DLAGENTS` in `makepkg.conf`(5). If present, `%u` will be replaced + with the download URL (correctly quoted), otherwise the download + URL will be appended to the end of the command. If present, `%o` + will be replaced with the local filename that it should be + downloaded to. + +## SEE ALSO + +librefetch(8), makepkg.conf(5) diff --git a/src/librefetch/librefetchdir/Makefile b/src/librefetch/librefetchdir/Makefile new file mode 100644 index 0000000..58116bb --- /dev/null +++ b/src/librefetch/librefetchdir/Makefile @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# librefetchdir/Makefile +# +# Copyright (C) 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# new = +librefetchdir := $(dir $(lastword $(MAKEFILE_LIST))) + +old_makepkg := $(shell which makepkg) +old_library := $(shell $(shell grep LIBRARY= $(old_makepkg)); echo $$LIBRARY) + +new_makepkg = $(new)/makepkg +new_library = $(new)/libmakepkg + +targets += $(new_makepkg) +targets += $(patsubst $(old_library)/%,$(new_library)/%,$(shell find $(old_library) -type f)) +targets += $(new_library)/tidy/~source_date_epoch.sh + +all: $(targets) +.PHONY: all + +.SECONDARY: +.DELETE_ON_ERROR: + +$(new_makepkg): $(old_makepkg) $(librefetchdir)/makepkg.gen + <$^ | install -Dm755 /dev/stdin $@ + +$(new_library)/source.sh: \ +$(new_library)/%: $(old_library)/% $(librefetchdir)/libmakepkg/%.gen + <$^ | install -Dm755 /dev/stdin $@ + +$(new_library)/tidy/purge.sh $(new_library)/tidy/~source_date_epoch.sh: \ +$(new_library)/%: $(librefetchdir)/libmakepkg/% + mkdir -p $(@D) + ln -sfT $(abspath $< $@) + +$(new_library)/%: $(old_library)/% + mkdir -p $(@D) + ln -sfT $(abspath $< $@) diff --git a/src/librefetch/librefetchdir/libmakepkg/source.sh.gen b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen new file mode 100755 index 0000000..269f9fd --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen @@ -0,0 +1,25 @@ +#!/usr/bin/sed -rf +# librefetchdir/makepkg.gen +# +# Copyright (C) 2013, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +/download_sources\(\) \{/ { + arm -rf "$srcdir"\nmkdir "$srcdir" +} diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh new file mode 100755 index 0000000..acb0030 --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh @@ -0,0 +1,58 @@ +#!/usr/bin/bash +# +# purge.sh - Remove unwanted files from the package +# +# Copyright (c) 2008-2016 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This file is part of LibreFetch +# +# 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/>. +# + +[[ -n "$LIBMAKEPKG_TIDY_PURGE_SH" ]] && return +LIBMAKEPKG_TIDY_PURGE_SH=1 + +LIBRARY=${LIBRARY:-'/usr/share/makepkg'} + +source "$LIBRARY/util/message.sh" +source "$LIBRARY/util/option.sh" + + +packaging_options+=('purge') +tidy_remove+=('tidy_purge') + +tidy_purge() { + if check_option "purge" "y" && [[ -n ${PURGE_TARGETS[*]} ]]; then + msg2 "$(gettext "Purging unwanted files...")" + 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 + fi +} diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh new file mode 100755 index 0000000..9141d67 --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh @@ -0,0 +1,38 @@ +#!/usr/bin/bash +# +# librefetchdir/libmakepkg/tidy/~source_date_epoch.sh +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# This filename starts with a ~ because it sorts after every other +# (printable) ASCII character, and we want this to run last. + +[[ -n "$LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH" ]] && return +LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH=1 + +tidy_modify+=('tidy_source_date_epoch') + +tidy_source_date_epoch() { + local date='1990-01-01 0:0:0 +0' + if [[ -n "$SOURCE_DATE_EPOCH" ]]; then + date="@$SOURCE_DATE_EPOCH" + fi + find . -exec touch --no-dereference --date="$date" -- {} + +} diff --git a/src/librefetch/librefetchdir/makepkg.gen b/src/librefetch/librefetchdir/makepkg.gen new file mode 100755 index 0000000..8928d91 --- /dev/null +++ b/src/librefetch/librefetchdir/makepkg.gen @@ -0,0 +1,42 @@ +#!/usr/bin/sed -rf +# librefetchdir/makepkg.gen +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +/LIBRARY=/iexport LIBRARY="${0%/*}/libmakepkg" + +/create_package\(\) \{/,/^\}$/ { + /pkg_file=/d # allow us to set pkg_file + s/"?\$\{comp_files\[@\]\}"?// # do not include .{PKGINFO,BUILDINGO,CHANGELOG,INSTALL,MTREE} + # This is long/gross. What it does: + # - pass --format=ustar to bsdtar, to inhibit it using the pax format + # - take the files that would be included in the tarball, and use + # find/sort/--files-from to order them for bsdtar + s/bsdtar(.*) - ([^|]*) \|/find \2 -print0 | LC_ALL=C sort --zero-terminated | bsdtar --null --files-from - --format=ustar --no-recursion \1 - |/ + s/create_signature .*/&; return $?/ # do not procede to create symlinks +} + +s|Making package:|Making source:| +s|Checking runtime dependencies\.\.\.|Checking source dependencies...| + /Checking buildtime dependencies\.\.\./d + +s|srcdir=.*|&-libre| +s|pkgdirbase=.*|&-libre| +s|check_build_status$|:| diff --git a/src/libretools.conf b/src/libretools.conf new file mode 100644 index 0000000..fb4593c --- /dev/null +++ b/src/libretools.conf @@ -0,0 +1,72 @@ +#!/hint/bash + +################################################################################ +# misc # +################################################################################ + +# The dir where you work on +WORKDIR="$LIBREHOME/packages" + +## Blacklist URL +BLACKLIST=https://projects.parabola.nu/blacklist.git/plain/blacklist.txt + +## Diff tool (vimdiff, gvimdiff, meld, etc) +## Used by `aur`, `diff-unfree` +DIFFPROG=$(which $([ -z "${DISPLAY:-}" ]||echo kdiff3 meld gvimdiff) vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q') + +## The architectures you'll be packaging for +## Used by `librestage`, `xbs-abslibre` +ARCHES=($(printf '%s\n' /usr/share/pacman/defaults/pacman.conf.*|sed 's|.*\.||')) + +## ABSLibre +# Used by xbs-abslibre +ABSLIBRERECV=git://projects.parabola.nu/abslibre/abslibre.git +ABSLIBRESEND=ssh://git@projects.parabola.nu/srv/git/abslibre/abslibre.git +ABSLIBREDEST="$WORKDIR/staging/abslibre" + +################################################################################ +# librerelease # +################################################################################ + +## Where to upload packages to +# '/staging/' is appended; this is for compatibility with previous versions. +REPODEST=repo@parabola.nu:staging/$LIBREUSER + +## These are run before and after uploading packages +HOOKPRERELEASE="ssh -fN ${REPODEST%%:*}" +HOOKPOSTRELEASE="sudo librechroot clean-repo" + +################################################################################ +# dagpkg # +################################################################################ + +# Note: Not being set is valid for any of the HOOK* settings. + +# Run a command before running FULLBUILDCMD +HOOKPREBUILD="" + +## 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 +# successfully. When run, it is given a repository name as a single argument. +HOOKLOCALRELEASE="librestage" + +################################################################################ +# toru # +################################################################################ + +TORUPATH=/var/lib/libretools/toru + +## The repos you'll be packaging for +## Used by: `toru-path` +# Tip: As early repos take precedence on $REPOS loops, you can use this as +# inverted order of precedence. Put testing repos first so dagpkg will 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') diff --git a/src/pkgbuild-check-nonfree b/src/pkgbuild-check-nonfree new file mode 100755 index 0000000..67f07bc --- /dev/null +++ b/src/pkgbuild-check-nonfree @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +# -*- tab-width: 4 ; sh-basic-offset: 4 -*- +# pkgbuild-check-nonfree + +# Copyright (C) 2011 Joseph Graham (Xylon) <joe@t67.eu> +# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +# I appologize that this program got *huge*. +# It's not complicated, just long. + +. "$(librelib messages)" +. "$(librelib conf)" +. "$(librelib blacklist)" + +usage() { + print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" + print "Analyzes a PKGBUILD for freedom issues" + 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 "Warning-level freedom issues:" + print " 2: Uses unrecognized licenses, check them" + print " 4: Uses GPL-incompatible licenses" + print "Error-level freedom issues:" + print " 8: Uses known unacceptable licenses" + print " 16: Has nonfree dependencies" + print " 32: Is a known nonfree package" + echo + print "Options:" + flag '-c' 'Use the cached blacklist, do not try downloading' + flag '-f' 'Allow running as root user' + echo + flag '-q' 'Be quiet' + flag '-v' 'Be verbose' + echo + flag '-h' 'Show this message' +} +# Make sure these match pkgbuild-summarize-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 + +main() { + # Parse flags + local cache=false + local asroot=false + local v=1 + while getopts 'cfqvh' arg; do + case "$arg" in + c) cache=true;; + f) asroot=true;; + q) v=0;; + v) v=2;; + h) usage; return $_E_OK;; + *) usage >&2; return $_E_ERROR;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + pkgbuilds=("`pwd`/PKGBUILD") + else + pkgbuilds=("$@") + fi + + # Do a check to see if we are running as root + if [[ -w / ]] && ! $asroot; then + error "Run as normal user, or use the -f option to run as root." + return 1 + fi + + # Adjust the verbosity + if [[ $v == 0 ]]; then + error() { :; } + warning() { :; } + plain() { :; } + info() { :; } + elif [[ $v == 1 ]]; then + info() { :; } + elif [[ $v == 2 ]]; then + info() { plain "$@"; } + fi + + # Update the blacklist + $cache || blacklist-update || return $_E_ERROR + + # Do the work + declare -i ret=0 + local pkgbuild + for pkgbuild in "${pkgbuilds[@]}"; do + pkgbuild_check "$pkgbuild" || ret=$(($ret|$?)) + done + return $ret +} + +# Helper functions ############################################################# +# These should maybe be moved into lib/conf.sh + +# Usage: var="$(pkgbuild_get_pkg_str ${pkgname} ${varname})" +# Gets a package-level string for a split-package +pkgbuild_get_pkg_str() { + [[ $# == 2 ]] || panic 'malformed call to pkgbuild_get_pkg_str' + local pkg=$1 + local var=$2 + + local indirect=${!var} + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + printf '%s' "${indirect}" +} +# Usage: eval $(pkgbuild_get_pkg_ary ${pkgname} ${varname} [$variable_name_to_set]) +# Gets a package-level array for a split-package +pkgbuild_get_pkg_ary() { + [[ $# == 2 ]] || [[ $# == 3 ]] || panic 'malformed call to pkgbuild_get_pkg_ary' + local pkg=$1 + local var=$2 + local out="${3:-$var}" + + local ary="${var}[@]" + local indirect=("${!ary}") + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + declare -p indirect|sed "s/ indirect=/ ${out}=/" +} + +# Checker functions ############################################################ + +# Usage: check_lic "${licence}" +# Check a license name to see if it is OK +check_lic() { + [[ $# == 1 ]] || panic 'malformed call to check_license' + local license=$1 + + info 'Checking license: %s' "$license" + + if [[ -e "/usr/share/licenses/common/$license" ]]; then + return $_E_OK + else + case "${license#custom:}" in + WTFPL) + # accept as common, I think it should be in the licenses package + return $_E_OK;; + BSD1|BSD2|BSD3|MIT|X11) + # accept these as common; they can't be included in the + # 'licenses' package because some text must be customized + return $_E_OK;; + BSD4) + warning "The 4-clause BSD license is free but has practical problems." + return $_E_LIC_NOGPL;; + BSD) + warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." + return $_E_LIC_UNKNOWN;; + JSON) + error "License '%s' is a known non-free license." "$license" + return $_E_LIC_NONFREE;; + *) + warning "License '%s' is not a common (recognized) license." "$license" + return $_E_LIC_UNKNOWN;; + esac + fi + panic 'code should never be reached' +} + +# Usage: check_dep "${dependency}" +# Checks for ${dependency} in the blacklist +check_dep() { + [[ $# == 1 ]] || panic 'malformed call to check_dep' + local pkg=$1 + + local line="$(blacklist-cat|blacklist-lookup "$pkg")" + local rep="$(blacklist-get-rep <<<"$line")" + if [[ -z $line ]]; then + # not mentioned in blacklist; free + info '%s: not blacklisted' "$pkg" + return $_E_OK + elif [[ -z $rep ]]; then + # non-free with no replacement + plain '%s: blacklisted' "$pkg" + return $_E_DEP_NONFREE + else + # non-free with free replacement + if [[ "$rep" == "$pkg" ]]; then + info '%s: repackaged with the same name' "$pkg" + else + info '%s: replaced by %s' "$pkg" "$rep" + fi + return $_E_OK + fi + panic 'code should never be reached' +} + +# Usage: check_pkg "${pkgname}" +# Checks for ${pkgname} in the blacklist +check_pkg() { + [[ $# == 1 ]] || panic 'malformed call to check_pkg' + check_dep "$@" + case $? in + $_E_OK) + return $_E_OK;; + $_E_DEP_NONFREE) + return $_E_PKG_NONFREE;; + *) + panic 'unexpected return code from check_dep';; + esac + panic 'code should never be reached' +} + +# Usage: pkgbuild_ckec $pkgbuild +# Check whether a PKGBUILD has any issues (using the above) +pkgbuild_check() ( + [[ $# == 1 ]] || panic 'malformed call to pkgbuild_check' + local pkgbuild=$1 + + load_PKGBUILD "$pkgbuild" + if [[ -z $pkgname ]]; then + return $_E_ERROR # not a PKGBUILD + fi + + declare -i ret=0 # the return status + local dep lic # iterators for us in `for` loops + local ck_deps ck_lics # lists of deps and licenses that have been checked + + if [[ ${#pkgname[@]} == 1 ]]; then + msg2 'Inspecting package pkgname=%q (%s)' "$pkgname" "$(get_full_version)" + else + msg2 'Inspecting split package pkgbase=%q (%s)' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + fi + + # Check if this is blacklisted + check_pkg "${pkgbase:-${pkgname[0]}}" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" \ + "${optdepends[@]%%:*}" "${mkdepends[@]}" + do + check_dep "$dep" || ret=$(($ret|$?)) + ck_deps+=("$dep") + done + # Check the licenses + for lic in "${license[@]}"; do + check_lic "$lic" || ret=$(($ret|$?)) + ck_lics+=("$lic") + done + + if [[ ${#pkgname[@]} == 1 ]]; then + # Non-split package + # Make sure a license is set + if [[ ${#ck_lics[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + else + # Split package + # Check the individual split packages + local _pkgname _license _depends _optdepends + for _pkgname in "${pkgname[@]}"; do + msg2 'Inspecting split package pkgname=%q (%s)' "$_pkgname" "$(get_full_version "$_pkgname")" + eval $(pkgbuild_get_pkg_ary "$_pkgname" license _license) + eval $(pkgbuild_get_pkg_ary "$_pkgname" depends _depends) + eval $(pkgbuild_get_pkg_ary "$_pkgname" optdepends _optdepends) + + # Check if this is blacklisted + check_pkg "$_pkgname" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${_depends[@]}" "${_optdepends[@]%%:*}"; do + if ! in_array "$dep" "${ck_deps[@]}"; then + check_dep "$dep" || ret=$(($ret|$?)) + fi + done + # Check the licenses + for lic in "${_license[@]}"; do + if ! in_array "$lic" "${ck_lics[@]}"; then + check_lic "$lic" || ret=$(($ret|$?)) + fi + done + + if [[ ${#_license[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + done + fi + + return $ret +) + +main "$@" diff --git a/src/pkgbuild-summarize-nonfree b/src/pkgbuild-summarize-nonfree new file mode 100755 index 0000000..1e39e87 --- /dev/null +++ b/src/pkgbuild-summarize-nonfree @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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 messages)" + +# Make sure these match pkgbuild-check-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 + +usage() { + print "Usage: %s [OPTIONS] STATUS" "${0##*/}" + print "Summarizes a status code from pkgbuild-check-nonfree" + echo + prose 'It thresholds the issues it finds, only failing for error-level + issues, and ignoring warnings. Unless `-q` is specified, it also + prints a summary of the issues it found.' + echo + print 'Options:' + flag '-q' 'Be quiet' + flag '-h' 'Show this message' +} + +main() { + local quiet=false + while getopts 'qh' arg; do + case "$arg" in + q) quiet=true;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -ne 1 ]]; then + usage >&2 + return 1 + fi + if ! [[ $1 =~ ^[0-9]+$ ]]; then + error 'STATUS must be an integer' + usage >&2 + return 1 + fi + + if $quiet; then + error() { :; } + warning() { :; } + fi + + parse $1; + return $? +} + +parse() { + [[ $# == 1 ]] || panic 'malformed call to parse' + declare -i s=$1; + + declare -i ret=0 + declare -i i + for i in 1 2 4 8 16 32; do + if [[ $(($s & $i)) -gt 0 ]]; then + case $i in + $_E_ERROR) + # could be anything, assume the worst + error "There was an error processing the PKGBUILD" + ret=1;; + $_E_LIC_UNKNOWN) + warning "This PKGBUILD has an unknown license";; + $_E_LIC_NOGPL) + warning "This PKGBUILD has a GPL-incompatible license";; + $_E_LIC_NONFREE) + error "This PKGBUILD has a known nonfree license" + ret=1;; + $_E_DEP_NONFREE) + error "This PKGBUILD depends on known nonfree packages" + ret=1;; + $_E_PKG_NONFREE) + error "This PKGBUILD is for a known nonfree package" + ret=1;; + esac + fi + done + return $ret +} + +main "$@" diff --git a/src/repo-diff b/src/repo-diff new file mode 100755 index 0000000..d11732b --- /dev/null +++ b/src/repo-diff @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Shows a diff between repo databases + +# Copyright (C) 2013 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# 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/>. + +. libremessages + +usage() { + print "Usage: %s arch/core/i686 parabola/core/i686" "${0##*/}" + print "Compares two repo databases using distro/repo/architecture format." + echo + print 'Shortcuts:' + flag 'arch' 'expands to Arch Linux repo url' + flag 'parabola' 'expands to Parabola GNU/Linux-libre repo url' +} + +if test $# -eq 0; then + usage + exit 0 +fi + +b() { bsdtar ztf $1 | cut -d "/" -f1 | sort -u ; } +n() { echo "$1".db | tr "/" "-"; } + +# hopefully simple way to convert +# parabola/libre/i686 +# to +# http://repo.parabola.nu/libre/os/i686/libre.db +# add more distros here +g() { + echo "$1" | sed -e "s,^\([^/]\+\)/\([^/]\+\)/\([^/]\+\)$,\1/\2/os/\3/\2.db," \ + -e "s,^parabola/,http://repo.parabola.nu/," \ + -e "s,^arch\(linux\)\?/,http://mirrors.kernel.org/archlinux/," +} + +mkdir ${0##*/}.$$ +pushd ${0##*/}.$$ >/dev/null + +d="" +for i in $1 $2; do + n=$(n "$i") + + test -z "$n" && exit 1 + + wget -O "$n" -nv $(g "$i") + b "$n" >${n}.orig + + d+=" ${n}.orig" +done + +{ + printf "$(gettext "Difference between %s and %s")\n---\n" $1 $2 + which diffstat &>/dev/null && diff -auN "${d[@]}" | diffstat + diff -auN "${d[@]}" +} >../${n}.diff + +popd >/dev/null +rm -r ${0##*/}.$$ + +print "Difference save on %s" "${n}.diff" diff --git a/src/toru/Makefile b/src/toru/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/toru/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/toru/toru-info b/src/toru/toru-info new file mode 100755 index 0000000..a7081cb --- /dev/null +++ b/src/toru/toru-info @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Prints info about a given pkgname + +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# 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/>. + +. libremessages +. "$(librelib conf)" + +for _pkg in "$@"; do + _pkgbuild="$(toru-where $_pkg)" + + if [ -f "$_pkgbuild/PKGBUILD" ]; then + if ! load_PKGBUILD "$_pkgbuild/PKGBUILD" 2>/dev/null; then + warning "Errors on %s" "$_pkg" + continue + fi + + deps=("${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}") + repo="$(basename -- "$(dirname -- "$_pkgbuild")")" + + msg "%s/%s %s-%s" "$repo" "$_pkg" "$pkgver" "$pkgrel" + msg2 '%s' "$pkgdesc" + msg2 '%s' "$url" + msg2 'Depends: %s' "${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..4600a5c --- /dev/null +++ b/src/toru/toru-path @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# 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/>. + +. libremessages +setup_traps + +# TODO: better option parsing +TORUPATH=${T:-${TORUPATH}} +VERBOSE=${V:-false} +FORCE=${F:-false} + +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools TORUPATH REPOS || exit 1 +load_files abs +check_vars abs ABSROOT || exit 1 + +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: ability to use flags to pass in other directories to fullrepos + +# This loops over ${REPOS[@]} backward. This is because early entries +# in REPOS have higher precidence, but the way this is implemented, +# the later entries have precedence, so we need to flip the order. +fullrepos=() +for (( i = ${#REPOS[@]}-1 ; i >= 0 ; i-- )); do + $VERBOSE && msg "Processing [%s]" "${REPOS[$i]}" + + # ABSROOT has trailing slash + if [ -d "${ABSROOT}${REPOS[$i]}" ]; then + fullrepos+=("${ABSROOT}${REPOS[$i]}") + fi +done + +# Find PKGBUILDs in ${fullrepos[@]} +find_args=("${fullrepos[@]}" -mindepth 2 -maxdepth 3 -type f -name PKGBUILD) +if ! $FORCE && [[ -e $lastsyncfile ]]; then + # if lastfilesync exists, only look at things that have + # changed since then (unless $FORCE is on) + find_args+=(-newer "${lastsyncfile}") +fi +IFS=$'\n' +pkgbuilds=($(find "${find_args[@]}")) + +# Add information from each of the PKGBUILDs to the toru cache. +msg "Updating path cache" +msg2 "%d PKGBUILDs to update" ${#pkgbuilds[@]} +for pkgbuild in "${pkgbuilds[@]}"; do + # plain "$_pkgbuild" + if ! load_PKGBUILD "${_pkgbuild}" >/dev/null 2>&1; then + error "%q contains errors, skipping" "${_pkgbuild}" + continue + fi + + fullpath="$(dirname -- "${_pkgbuild}")" + + for _pkg in "${pkgbase}" "${pkgname[@]}" "${provides[@]}"; do + $VERBOSE && msg2 '%s -> %s' "${_pkg}" "${fullpath}" + tcamgr put "${pathfile}" "${_pkg%%[<>=]*}" "${fullpath}" + done +done + +date +%s > "${lastsyncfile}" diff --git a/src/toru/toru-where b/src/toru/toru-where new file mode 100755 index 0000000..f6df15f --- /dev/null +++ b/src/toru/toru-where @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Locates a PKGBUILD dir on toru's path cache + +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# 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 TORUPATH || exit 1 + +tcamgr get "${TORUPATH}/paths.tch" "$1" 2>/dev/null || echo "" diff --git a/src/workflows.md b/src/workflows.md new file mode 100644 index 0000000..03dca4f --- /dev/null +++ b/src/workflows.md @@ -0,0 +1,64 @@ +# Workflows + +Describe your packaging workflow here! + + +## fauno's way + +During packaging, I don't usually restart a build from scratch if I have to make +changes to the PKGBUILD. I use a lot of commenting out commands already ran, +`makepkg -R`, etc. When I used `libremakepkg` I ended up using a lot more +`librechroot` and working from inside the unconfigured chroot, because +`makechrootpkg` (the underlying technology for `libremakepkg`) tries to be too +smart. + +When I started writing `treepkg` I found that mounting what I need directly on +the chroot and working from inside it was much more comfortable and simple than +having a makepkg wrapper doing funny stuff (for instance, mangling +`makepkg.conf` and breaking everything.) + +This is how the chroot is configured: + +* Create the same user (with same uid) on the chroot that the one I use + regularly. + +* Give it password-less sudo on the chroot. + +* Bind mount `/home` to `/chroot/home`, where I have the abslibre-mips64el + clone. + +* Bind mount `/var/cache/pacman/pkg` to `/chroot/var/cache/pacman/pkg` + +* Put these on system's `fstab` so I don't have to do it everytime + +* Configure `makepkg.conf` to `PKGDEST=CacheDir` and `SRCDEST` to something on + my home. + +Workflow: + +* Enter the chroot with `systemd-nspawn -D/chroot` and `su - fauno`. + +* From another shell (I use tmux) edit the abslibre or search for updates with + `git log --no-merges --numstat`. + +* Pick a package and run `treepkg` from its dir on the chroot, or retake a build + with `treepkg /tmp/package-treepkg-xxxx`. + + > Note: `treepkg` has been deprecated in favor of `dagpkg`. + +What this allows: + +* Not having to worry about the state of the chroot. `chcleanup` removes and + adds packages in a smart way so shared dependencies stay and others move along + (think of installing and removing qt for a complete kde rebuild). + +* Building many packages in a row without recreating a chroot for every one of + them. + +* Knowing that any change you made to the chroot stays as you want (no one + touches your makepkg.conf) + +* Hability to run regular commands, not through a chroot wrapper. I can `cd` to + a dir and use `makepkg -whatever` on it and nothing breaks. + +* No extra code spent on wrappers. diff --git a/src/xbs-abs/.gitignore b/src/xbs-abs/.gitignore new file mode 100644 index 0000000..d472431 --- /dev/null +++ b/src/xbs-abs/.gitignore @@ -0,0 +1,3 @@ +commitpkg* +archrelease* +!*.patch diff --git a/src/xbs-abs/Makefile b/src/xbs-abs/Makefile new file mode 100644 index 0000000..248848f --- /dev/null +++ b/src/xbs-abs/Makefile @@ -0,0 +1,27 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir)/xbs +pkglibexecdir = $(libexecdir)/xbs + +_helpers = archrelease commitpkg +libretools-bins = +libretools-libexecs = helper-abs +pots += $(_helpers) +devtools-files += $(addsuffix .in,$(_helpers)) +am_out_files += $(_helpers) +am_sys_files += $(addprefix $(pkglibexecdir)/helper-abs.d/,$(_helpers)) + +$(outdir)/commitpkg: $(srcdir)/commitpkg.in + @echo "OUT $@" + @{ \ + echo '#!/usr/bin/env bash'; \ + echo '. "$$(librelib common)"'; \ + echo '. ./PKGBUILD'; \ + echo 'repo=$$1; arch=$$2;'; \ + sed -n "/== 'any'/,\$$p" $<; \ + } | install -Dm755 /dev/stdin $@ + +$(DESTDIR)$(pkglibexecdir)/helper-abs.d/%: $(srcdir)/% + install -Dm755 '$<' '$@' + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs-abs/archrelease.patch b/src/xbs-abs/archrelease.patch new file mode 100644 index 0000000..bdf2deb --- /dev/null +++ b/src/xbs-abs/archrelease.patch @@ -0,0 +1,8 @@ +--- archrelease.in 2014-06-20 09:42:13.367553434 -0400 ++++ archrelease.ugly 2014-06-27 17:42:14.392917837 -0400 +@@ -1,4 +1,5 @@ + #!/bin/bash ++# License: Unspecified + + m4_include(lib/common.sh) + m4_include(lib/valid-tags.sh) diff --git a/src/xbs-abs/helper-abs b/src/xbs-abs/helper-abs new file mode 100755 index 0000000..d7e2499 --- /dev/null +++ b/src/xbs-abs/helper-abs @@ -0,0 +1,201 @@ +#!/usr/bin/env bash + +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For code from db-functions (arch_svn): +# Copyright (C) 2012 Pierre Schmitz <pierre@archlinux.de> +# For code from db-move+db-remove (move+unrelease): +# Copyright (C) 2008-2009 Aaron Griffin <aaronmgriffin@gmail.com> +# Copyright (C) 2009 Abhishek Dasgupta <abhidg@gmail.com> +# Copyright (C) 2009, 2011 Dan McGee <dan@archlinux.org> +# Copyright (C) 2009-2012 Pierre Schmitz <pierre@archlinux.de> +# For code just from db-move (move): +# Copyright (C) 2011 Rémy Oudompheng <remyoudompheng@gmail.com> +# Copyright (C) 2012 Florian Pritz <bluewind@xinu.at> +# For code just from db-remove (unrelease): +# Copyright (C) 2009 Eric Bélanger <snowmaniscool@gmail.com> +# Copyright (C) 2011 Florian Pritz <bluewind@xinu.at> +# +# License: GNU GPLv2+ +# +# 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/>. + +load_config() { + . "$(librelib conf.sh)" + load_files xbs-abs + # SVNUSER is optional + check_vars xbs-abs SVNDIR SVNREPOS ARCHES || exit 1 +} + +# This is taken from dbscripts:db-fuctions +arch_svn() { + if [[ -z "${SVNUSER}" ]]; then + /usr/bin/svn "${@}" + else + sudo -u "${SVNUSER}" -- /usr/bin/svn --username "${USER}" "${@}" + fi +} + +pac2svn() { + local pacrepo=$1 + + # Figure out which svn repo we need + local svnrepoStr + for svnrepoStr in "${SVNREPOS[@]}"; do + local svnrepoAry=($svnrepoStr) + local svnrepo=${svnrepoAry[0]} + local svnurl=${svnrepoAry[1]} + local pacrepos=("${svnrepoAry[@]:2}") + + if in_array "$pacrepo" "${pacrepos[@]}"; then + echo "$svnrepo" + return 0 + fi + done + return 1 +} + +status() { + load_config + [[ -z $(arch_svn status -q) ]] +} + +download() { + load_config + + local svnrepoStr + for svnrepoStr in "${SVNREPOS[@]}"; do + local svnrepoAry=($svnrepoStr) + local svnrepo=${svnrepoAry[0]} + local svnurl=${svnrepoAry[1]} + local pacrepos=("${svnrepoAry[@]:2}") + + if [[ -d "$SVNDIR/$svnrepo/.svn" ]]; then + arch_svn -q up "$SVNDIR/$svnrepo"/* + else + # checkout non-recursive, then lazy load + # necessary because: + # > DO NOT CHECK OUT THE ENTIRE SVN REPO. Your address + # > may be blocked. + arch_svn -q checkout -N "$svnurl" "$SVNDIR/$svnrepo" + fi + done +} + +release-client() { + local repo=$1 + local arch=$2 + + # Hack to use arch_svn as 'svn' in external scripts + local tmpdir="$(mktemp -dt "xbs-abs-release.XXXXXXXXXX")" + trap "$(printf 'rm -rf -- %q' "$tmpdir")" EXIT + printf '%s\n' \ + '#!/bin/bash' \ + "$(declare -p SVNUSER 2>/dev/null)" \ + "$(declare -f arch_svn)" \ + 'arch_svn "$@"' \ + > "$tmpdir/svn" + chmod 755 "$tmpdir/svn" + export PATH="$tmpdir:$PATH" + + "${0}.d/archrelease" -f "${repo}-${arch}" + "${0}.d/commitpkg" "${repo}" "${arch}" +} + +release-server() { + # Do nothing + : +} + +unrelease() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + local tag="$repo-$arch" + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "$svndir" + + # This is based off code from dbscripts:db-remove + arch_svn rm --force -q "${svndir}/repos/${tag}" + arch_svn commit -q "${svndir}" -m "${0##*/}: $pkgbase removed by $(id -un)" +} + +move() { + local repo_from=$1 + local repo_to=$2 + local pkgbase=$3 + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "$svndir" + + local tag_list="" + local pkgarch + local arches=() + # this is based off code from dbscripts:db-move + for pkgarch in "${ARCHES[@]}" 'any'; do + local dir_from="${svndir}/repos/${repo_from}-${pkgarch}" + local dir_to="${svndir}/repos/${repo_to}-${pkgarch}" + + if [ -f "${dir_from}/PKGBUILD" ]; then + if [ -d "${dir_to}" ]; then + for file in $(arch_svn ls "${dir_to}"); do + arch_svn rm -q "${dir_to}/$file@" + done + else + mkdir "${dir_to}" + arch_svn add -q "${dir_to}" + fi + + local file + for file in $(arch_svn ls "${dir_from}"); do + arch_svn mv -q -r HEAD "${dir_from}/$file@" "${dir_to}/" + done + arch_svn rm --force -q "${dir_from}" + tag_list="$tag_list, $pkgarch" + arches+=("$pkgarch") + fi + done + tag_list="${tag_list#, }" + arch_svn commit -q "${svndir}" -m "${0##*/}: moved ${pkgbase} from [${repo_from}] to [${repo_to}] (${tag_list})" + echo "${arches[*]}" +} + +releasepath() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "${svndir}" + local releasepath="${svndir}/repos/${repo}-${arch}" + if [[ -f "${releasepath}/PKGBUILD" ]]; then + printf '%s\n' "$releasepath" + return 0 + fi + return 1 +} + +name() { + echo SVN +} + +case "$1" in + status|download|release-client|release-server|unrelease|move|releasepath|name) "$@";; + *) exit 127;; +esac diff --git a/src/xbs-abs/xbs-abs.conf b/src/xbs-abs/xbs-abs.conf new file mode 100644 index 0000000..6b1585f --- /dev/null +++ b/src/xbs-abs/xbs-abs.conf @@ -0,0 +1,13 @@ +SVNDIR=/var/lib/xbs-abs +#SVNUSER= + +_packages_repos=(core extra testing staging {gnome,kde}-unstable) +_community_repos=({community,multilib}{,-testing,-staging}) + +# name url repos... +SVNREPOS=( + "packages svn://svn.archlinux.org/packages ${_packages_repos[*]}" + "community svn://svn.archlinux.org/community ${_community_repos[*]}" +) + +ARCHES=(i686 x86_64) diff --git a/src/xbs-abslibre/Makefile b/src/xbs-abslibre/Makefile new file mode 100644 index 0000000..c946d5d --- /dev/null +++ b/src/xbs-abslibre/Makefile @@ -0,0 +1,8 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkglibexecdir = $(libexecdir)/xbs + +libretools-bins = +libretools-libexecs = helper-abslibre + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs-abslibre/helper-abslibre b/src/xbs-abslibre/helper-abslibre new file mode 100755 index 0000000..91e7361 --- /dev/null +++ b/src/xbs-abslibre/helper-abslibre @@ -0,0 +1,202 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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 +setup_traps +set -u + +lockarch() { + local arch=$1 + lock 9 "${ABSLIBREDEST}/${arch}.lock" \ + "Waiting for a lock on the ABSLibre release directory for %s" "${arch}" +} + +unlockarch() { + lock_close 9 +} + +checkgit() { + if [[ ! -d "${ABSLIBREDEST}/${arch}/.git" ]]; then + error 'Not a git repository: %s' "${ABSLIBREDEST}/${arch}" + exit 1 + fi +} + +conf() { + . "$(librelib conf)" + load_files libretools + check_vars libretools "$@" || exit 1 +} + +################################################################################ + +# Operates on the `abslibre` format[0]. On the client/developer side, +# release-client *copies* files from the `abslibre` tree to an +# `abstree`[1] tree located at `devbox:${ABSLIBREDEST}/${arch}`. On +# the server side, after those `abstree`s have been uploaded +# (ssh/rsync), release-server *copies* them out of the `abstree` into +# a different `abslibre` on the server, similarly located at +# `repobox:${ABSLIBREDEST}/${arch}`. +# +# [0]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abslibre.md +# [1]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abstree.md + +# The number of arguments and CWD constraints are enforced by the main +# XBS program, no need to check those things here! + +# Args: none +# CWD: a directory with a PKGBUILD, in an 'abslibre' tree. +status() { + [[ -z $(git status -s .) ]] +} + +# Args: none +# CWD: anywhere +# Host: developer box +download() { + conf WORKDIR ABSLIBRERECV ABSLIBRESEND + + gitget -f -p "$ABSLIBRESEND" checkout "$ABSLIBRERECV" "$WORKDIR/abslibre" +} + +# Args: REPO ARCH +# CWD: a directory with a PKGBUILD, in an 'abslibre' tree. +# Host: developer box +release-client() { + local repo=$1 + local arch=$2 + + conf ABSLIBREDEST + local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")" + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + + if [[ -e $pkgdir ]]; then + rm -rf -- "$pkgdir" + fi + + mkdir -p -- "$pkgdir" + git ls-files -z | xargs -0 -I{} cp -- {} "$pkgdir" +} + +# Args: REPO ARCH +# CWD: a directory with a PKGBUILD, outside of ${ABSLIBREDEST} +# Host: repo +release-server() { + local repo=$1 + local arch=$2 + + conf ABSLIBREDEST + local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")" + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + checkgit + + if [[ -e $pkgdir ]]; then + rm -rf -- "$pkgdir" + fi + + mkdir -p -- "$pkgdir" + cp -- * "$pkgdir" + + cd "$pkgdir" + git add . + git commit -q -m "xbs-abslibre: Release ${repo}/${pkgbase} for ${arch} (by $(id -un))" +} + +# Args: PKGBASE REPO ARCH +# CWD: anywhere +# Host: repo +unrelease() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + conf ABSLIBREDEST + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + checkgit + + if [[ -f "${pkgdir}/PKGBUILD" ]]; then + git rm -qrf -- "$pkgdir" + git commit -q -m "xbs-abslibre: Remove ${repo}/${pkgbase} from ${arch} (by $(id -un))" + fi +} + +# Args: FROMREPO TOREPO PKGBASE +# CWD: anywhere +# Host: repo +move() { + local repo_from=$1 + local repo_to=$2 + local pkgbase=$3 + + conf ABSLIBREDEST ARCHES + + # Execute each iteration in a subshell so that 'checkgit' + # bailing for an architecture doesn't abort the entire thing. + local arch + for arch in "${ARCHES[@]}" any; do ( + lockarch "$arch" + checkgit + + local dir_from="${ABSLIBREDEST}/${arch}/${repo_from}/${pkgbase}" + local dir_to="${ABSLIBREDEST}/${arch}/${repo_to}/${pkgbase}" + + if [[ -f "${dir_from}/PKGBUILD" ]]; then + if [[ -e "${dir_to}" ]]; then + git rm -qrf -- "$dir_to" + fi + mkdir -p -- "${dir_to%/*}" + git mv -- "$dir_from" "$dir_to" + git commit -q -m "xbs-abslibre: Move ${pkgbase} from ${repo_from} to ${repo_to} on ${arch} (by $(id -un))" + fi + unlock arch + ) done +} + +# Args: PKGBASE REPO ARCH +# CWD: anywhere +# Host: repo +releasepath() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + conf ABSLIBREDEST + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + + lockarch "$arch" + + if [[ -f "${pkgdir}/PKGBUILD" ]]; then + printf '%s\n' "$pkgdir" + return 0 + fi + + return 1 +} + +name() { + echo ABSLibre +} + +case "$1" in + status|download|release-client|release-server|unrelease|move|releasepath|name) "$@" || exit $?;; + *) exit 127;; +esac diff --git a/src/xbs/Makefile b/src/xbs/Makefile new file mode 100644 index 0000000..974586e --- /dev/null +++ b/src/xbs/Makefile @@ -0,0 +1,5 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir)/xbs + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs/xbs b/src/xbs/xbs new file mode 100755 index 0000000..6319110 --- /dev/null +++ b/src/xbs/xbs @@ -0,0 +1,186 @@ +#!/usr/bin/env bash + +# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# 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/>. + +default_libdir=/usr/lib/xbs + +. libremessages +. "$(librelib conf)" + +errusage() { + if [[ $# -gt 0 ]]; then + error "$@" + fi + usage >&2 + exit 1 +} + +usage() { + print 'Usage: %s [-b BUILDSYSTEM|-h] COMMAND [ARGUMENTS]' "${0##*/}" + print 'Tool for working with arbitrary ABS-like build systems' + echo + prose 'This is a pluggable tool. The BUILDSYSTEM it uses is + configured in:' + bullet '/etc/xbs.conf' + bullet '${XDG_CONFIG_HOME}/xbs.conf' + bullet 'with the `-b` flag' + prose 'Later items take precedence over earlier ones.' + echo + prose 'It looks for a helper programs named helper-${BUILDSYSTEM}, in + the directory `%q` by default, but this directory can be changed + with the environmental variable XBS_LIBDIR.' "$default_libdir" + echo + print 'Options:' + flag "-b $(_ BUILDSYSTEM)" 'Use BUILDSYSTEM instead of the one + configured in xbs.conf' \ + '-h' 'Show this message' + echo + prose "Whether a command is intended for use on a developer's box or on + the server is indicated by (dev), (srv), or (any) before the + command" + echo + print 'Commands:' + flag "$(_ '(dev) status')" \ + 'Are there uncommitted changes in `.`?' \ + "$(_ '(dev) download')" \ + 'Download or update the tree' \ + "$(_ '(dev) release-client REPO ARCH')" \ + 'Release `.` (for developer boxes)' \ + "$(_ '(srv) release-server REPO ARCH')" \ + 'Release `.` (for server boxes)' \ + "$(_ '(srv) unrelease PKGBASE REPO ARCH')" \ + 'Unrelease a pkgbase' \ + "$(_ '(srv) move FROMREPO TOREPO PKGBASE')" \ + 'Move a pkgbase from one repo to another' \ + "$(_ '(srv) releasepath PKGBASE REPO ARCH')" \ + 'Print the path to the staged version of pkgbase, or exit with + non-zero if not released' \ + "$(_ '(any) name')" \ + 'Print a human-friendly version of the BUILDSYSTEM name' \ + "$(_ '(any) help')" \ + 'Show this message' +} + +status() { + if [[ ! -f PKGBUILD ]]; then + error 'PKGBUILD not found' + # Though in this file in general it doesn't matter, in + # this case it does: Using "exit" instead of "return" + # is imporant because it prevents flow returning to + # release-client. + exit 1 + fi + "$HELPER" status "$@" +} + +download() { + "$HELPER" download "$@" +} + +release-client() { + if ! status; then + error 'You have not committed your changes yet!' + exit 1 + fi + "$HELPER" release-client "$@" +} + +release-server() { + if [[ ! -f PKGBUILD ]]; then + error 'PKGBUILD not found' + exit 1 + fi + "$HELPER" release-server "$@" +} + +unrelease() { + "$HELPER" unrelease "$@" +} + +move() { + "$HELPER" move "$@" +} + +releasepath() { + "$HELPER" releasepath "$@" +} + +help() { + usage +} + +name() { + "$HELPER" name "$@" +} + +main() { + BUILDSYSTEM='' + while getopts 'b:h' arg; do + case $arg in + b) BUILDSYSTEM=$OPTARG;; + h) usage; return 0;; + *) errusage;; + esac + done + shift $(($OPTIND - 1)) + + if [[ -z $BUILDSYSTEM ]]; then + load_files xbs || return 1 + check_vars xbs BUILDSYSTEM || { + prose 'or specify the `-b` flag.' >&2 + return 1 + } + fi + + if [[ -z $XBS_LIBDIR ]]; then + export XBS_LIBDIR=$default_libdir + fi + + HELPER="${XBS_LIBDIR}/helper-${BUILDSYSTEM}" + if [[ ! -x "$HELPER" ]]; then + error 'No helper for build system found: %s' "$BUILDSYSTEM" + return 1; + fi + + if [[ $# -lt 1 ]]; then + errusage "Must specify a command" + fi + + if [[ -w / ]]; then + error 'Run as a normal user' + fi + + local cmd=$1; shift + case "$cmd" in + status|download|name|help) + [[ $# -eq 0 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + release-client|release-server) + [[ $# -eq 2 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + move|unrelease|releasepath) + [[ $# -eq 3 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + *) errusage 'unknown command: %s' "$cmd";; + esac +} + +main "$@" diff --git a/src/xbs/xbs.conf b/src/xbs/xbs.conf new file mode 100644 index 0000000..556c133 --- /dev/null +++ b/src/xbs/xbs.conf @@ -0,0 +1 @@ +BUILDSYSTEM=abslibre |