#!/usr/bin/env bash # librefetch # # Copyright (C) 2013-2014 Luke Shumaker # # 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 . . "$(librelib conf)" . "$(librelib messages)" setup_traps declare -r tempdir="$(mktemp -d --tmpdir ${0##*/}.XXXXXXXXXXX)" trap "rm -rf -- $(printf '%q' "$tempdir")" 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 looks at a build script, and uses that to create the source tarball. SOURCE_URL is ignored, except that it is used to set the default value of OUTPUT_FILE." 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.parabolagnulinux.org/other/mypackage/mypackage-1.0.tar.gz' "$cmd" echo print "Options:" print " Settings:" flag "-C" "Force create mode (don't download)" flag "-D" "Force download mode (don't create)" flag "-p <$(_ FILE)>" "Use an alternate build script (instead of 'PKGBUILD') If an SRCBUILD exists in the same directory, it is used instead" print " Alternate modes:" flag "-g, --geninteg" "Generate integrity checks for source files" flag "-S, --srcbuild" "Print the effective build script (SRCBUILD)" flag "-M, --makepkg" "Print the effective makepkg script" flag "-h, --help" "Show this message" } main() { BUILDFILE="$(readlink -m PKGBUILD)" makepkg_opts=() extra_opts=() mode=download-create if ! parse_options "$@"; then usage >&2 exit 1 fi # Mode: help ########################################################### if [[ $mode =~ help ]]; then usage exit 0 fi ######################################################################## makepkg="$(modified_makepkg "$(which makepkg)")" # Mode: makepkg ######################################################## if [[ $mode =~ makepkg ]]; then cat "$makepkg" exit 0 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 # 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="$(readlink -m -- "$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 PKGEXT=${dst##*/} export PKGEXT=${PKGEXT%.part} export PKGDEST=${dst%/*} export pkg_file=$dst cd "$BUILDFILEDIR" "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >&2 || exit $? 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="$(readlink -m -- "$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 ############################################################### # an ERE makepkg_modify=' /create_package\(\) \{/,/^\}$/ { /pkg_file=/d # allow us to set pkg_file s/"?\$\{comp_files\[@\]\}"?// # do not include .{PKGINFO,INSTALL,CHANGELOG} # 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/xargs to order them before passing them to 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 } /tidy_install\(\) \{/,/^\}$/ { /for .*PURGE_TARGETS/itidy_install_purge /for .*PURGE_TARGETS/,/done/d /^\}$/ifind . -exec touch --no-dereference --date="1990-01-01 0:0:0 +0" -- {} + } /download_sources\(\) \{/ { arm -rf "$srcdir"\nmkdir "$srcdir" } s|Making package:|Making source:| s:Checking (run|build)time dependencies\.\.\.:Checking source dependencies...: s|srcdir=.*|&-libre| s|pkgdirbase=.*|&-libre| s|check_build_status$|:| ' tidy_install_purge() { local pt for pt in "${PURGE_TARGETS[@]}"; do if [[ ${pt} = "${pt%/}" ]]; then if [[ ${pt} = "${pt//\/}" ]]; then find . ! -type d -name "${pt}" -exec rm -f -- '{}' + else rm -f "${pt}" fi else if [[ ${pt%/} = "${pt//\/}" ]]; then find . -type d -name "${pt%/}" -exec rm -rf -- '{}' + else rm -rf "${pt}" fi fi done } modified_makepkg() { local makepkg_orig=$1 local makepkg_mine="$tempdir/makepkg" { echo '#!/bin/bash' declare -f tidy_install_purge sed -r "$makepkg_modify" < "$makepkg_orig" } > "$makepkg_mine" chmod 755 "$makepkg_mine" printf "%s\n" "$makepkg_mine" } # Modify PKGBUILD ############################################################## # a string to be appended pkgbuild_append=' # do not do split packages if [[ ${#pkgname[@]} -gt 1 ]]; then if [[ -n $pkgbase ]]; then pkgname=("$pkgbase") else pkgname=("$pkgname") fi fi # copy source variables source=("${mksource[@]}") noextract=("${mknoextract[@]}") md5sums=("${mkmd5sums[@]}") sha1sums=("${mksha1sums[@]}") sha256sums=("${mksha256sums[@]}") sha384sums=("${mksha384sums[@]}") sha512sums=("${mksha512sums[@]}") depends=() checkdepends=() makedepends=("${mkdepends[@]}") #### options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx) PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) #### if ! declare -f mksource >/dev/null; then mksource() { :; } fi prepare() { :; } build() { mksource; } check() { :; } package() { cp -a "$srcdir"/*/ "$pkgdir/"; } ' modified_pkgbuild() { local pkgbuild=$1 local srcbuild="$tempdir/SRCBUILD" printf '%s' "$pkgbuild_append" | cat "$pkgbuild" - > "$srcbuild" printf '%s\n' "$srcbuild" } # Modify SRCBUILD ############################################################## modified_srcbuild() { local orig=$1 local new="$tempdir/SRCBUILD" sed -e '/PKGDEST=/d' -e '/PKGEXT=/d' < "$orig" > "$new" printf '%s\n' "$new" } main "$@"