#!/usr/bin/env bash # librefetch # # Copyright (C) 2013-2017 Luke Shumaker # # For just the create_signature() function: # Copyright (C) 2006-2013 Pacman Development Team # Copyright (C) 2002-2006 Judd Vinet # Copyright (C) 2005 Aurelien Foret # Copyright (C) 2006 Miklos Vajna # Copyright (C) 2005 Christian Hamar # Copyright (C) 2006 Alex Smith # Copyright (C) 2006 Andras Voroskoi # # 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 . # 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 $EXIT_INVALIDARGUMENT fi doit } doit() { # Mode: help ########################################################### if [[ $mode =~ help ]]; then usage exit $EXIT_SUCCESS fi ######################################################################## makepkg="$(modified_makepkg)" # Mode: makepkg ######################################################## if [[ $mode =~ makepkg ]]; then printf '%s\n' "$makepkg" exit $EXIT_SUCCESS 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 $EXIT_FAILURE 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 $EXIT_SUCCESS fi # Mode: srcbuild ####################################################### if [[ $mode =~ srcbuild ]]; then cat "$srcbuild" exit $EXIT_SUCCESS 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_conf librefetch.conf MIRRORS DOWNLOADER || exit # 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\"}" if { eval "$dlcmd"; } >&2; then exit $EXIT_SUCCESS fi 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%.sig} ]]; 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=$EXIT_SUCCESS local {shrt,long}{1,2} # makepkg options local makepkg_orig 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 long args shrt="$({ printf '%s\0' "${shrt1[@]}"; printf '%s:\0' "${shrt2[@]}"; } | sort -zu | xargs -0 printf '%s')" long="$({ printf '%s\0' "${long1[@]}"; printf '%s:\0' "${long2[@]}"; } | sort -zu | xargs -0 printf '%s,')" args="$(getopt -n "$cmd" -o "$shrt" -l "${long%,}" -- "$@")" || ret=$EXIT_INVALIDARGUMENT 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=$EXIT_INVALIDARGUMENT 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=$EXIT_INVALIDARGUMENT fi ;; esac return $ret } # Modify makepkg ############################################################### modified_makepkg() { local dir 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 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 new new="$(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=$EXIT_SUCCESS 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=$EXIT_FAILURE if (( ! ret )); then msg2 "Created signature file %s." "$filename.sig" else error "Failed to sign package file." return $ret fi } main "$@"