#!/bin/bash # Makefile.d/downloader: A downloader script that handles all kinds of # funny VCS URLs. # Very heavily based on Pacman's 'makepkg' script. # Usage: Makefile.d/downloader ARCH DEPNAME::URL [DEPNAME::URL...] # Copyright (c) 2014 Luke Shumaker # Copyright (c) 2006-2013 Pacman Development Team # Copyright (c) 2002-2006 by Judd Vinet # Copyright (c) 2005 by Aurelien Foret # Copyright (c) 2006 by Miklos Vajna # Copyright (c) 2005 by Christian Hamar # Copyright (c) 2006 by Alex Smith # Copyright (c) 2006 by Andras Voroskoi # # 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 . export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='/usr/share/locale' shopt -s extglob set -u -e -E # check if messages are to be printed using color declare ALL_OFF= BOLD= BLUE= GREEN= RED= YELLOW= if [[ -t 2 ]]; then # prefer terminal safe colored and bold text when tput is supported if tput setaf 0 &>/dev/null; then ALL_OFF="$(tput sgr0)" BOLD="$(tput bold)" BLUE="${BOLD}$(tput setaf 4)" GREEN="${BOLD}$(tput setaf 2)" RED="${BOLD}$(tput setaf 1)" YELLOW="${BOLD}$(tput setaf 3)" else ALL_OFF="\e[1;0m" BOLD="\e[1;1m" BLUE="${BOLD}\e[1;34m" GREEN="${BOLD}\e[1;32m" RED="${BOLD}\e[1;31m" YELLOW="${BOLD}\e[1;33m" fi fi readonly ALL_OFF BOLD BLUE GREEN RED YELLOW ### SUBROUTINES ### plain() { local mesg=$1; shift printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg() { local mesg=$1; shift printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg2() { local mesg=$1; shift printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } warning() { local mesg=$1; shift printf "${YELLOW}==> $(gettext "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } error() { local mesg=$1; shift printf "${RED}==> $(gettext "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } dir_is_empty() { ( shopt -s dotglob nullglob files=("$1"/*) (( ${#files} == 0 )) ) } # a "netfile" has the form: # "depname::url" get_url() { printf -- "%s\n" "${1#*::}"; } get_depname() { printf -- "%s\n" "${1%%::*}"; } get_protocol() { local url="$(get_url "$1")" local proto="${url%%://*}" proto=${proto%%+*} printf -- "%s\n" "$proto" } get_urlname() { local netfile=$1 local url="$(get_url "$netfile")" local proto=$(get_protocol "$netfile") urlname=${netfile%%#*} # Strip a fragment urlname=${urlname%/} # Strip a trailing slash urlname=${urlname##*/} # Strip leading components urlname=${urlname%.$proto} # Strip .git printf -- "%s\n" "${urlname}" } get_downloadname() { local netfile=$1 local depname="$(get_depname "$netfile")" local urlname="$(get_urlname "$netfile")" local proto="$(get_protocol "$netfile")" local name case $proto in git|hg|svn) name="$depname.$proto";; http|https) name="$urlname";; *) return 1;; esac printf -- "%s\n" "$name" } #### get_downloadclient() { local proto=$1 # loop through DOWNLOAD_AGENTS variable looking for protocol local i for i in "${DLAGENTS[@]}"; do local handler="${i%%::*}" if [[ $proto = "$handler" ]]; then local agent="${i##*::}" break fi done # if we didn't find an agent, return an error if [[ -z $agent ]]; then error "$(gettext "Unknown download protocol: %s")" "$proto" plain "$(gettext "Aborting...")" exit 1 # $E_CONFIG_ERROR fi # ensure specified program is installed local program="${agent%% *}" if [[ ! -x $program ]]; then local baseprog="${program##*/}" error "$(gettext "The download program %s is not installed.")" "$baseprog" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_PROGRAM fi printf "%s\n" "$agent" } download_file() { local netfile=$1 local filename="$(get_downloadname "$netfile")" pushd "$SRCDEST" &>/dev/null if [[ -f "$filename" ]]; then msg2 "$(gettext "Found %s")" "${filename}" return fi local proto=$(get_protocol "$netfile") # find the client we should use for this URL local dlcmd dlcmd=$(get_downloadclient "$proto") || exit $? local url=$(get_url "$netfile") msg2 "$(gettext "Downloading %s...")" "$filename" # temporary download file, default to last component of the URL local dlfile=$filename # replace %o by the temporary dlfile if it exists if [[ $dlcmd = *%o* ]]; then dlcmd=${dlcmd//\%o/\"$filename.part\"} dlfile="$filename.part" fi # add the URL, either in place of %u or at the end if [[ $dlcmd = *%u* ]]; then dlcmd=${dlcmd//\%u/\"$url\"} else dlcmd="$dlcmd \"$url\"" fi local ret=0 eval "$dlcmd || ret=\$?" if (( ret )); then [[ ! -s $dlfile ]] && rm -f -- "$dlfile" error "$(gettext "Failure while downloading %s")" "$filename" plain "$(gettext "Aborting...")" exit 1 fi # rename the temporary download file to the final destination if [[ $dlfile != "$filename" ]]; then mv -f "$dlfile" "$filename" fi } extract_file() { local netfile=$1 local depname="$(get_depname "$netfile")" local filename="$(get_downloadname "$netfile")" local filepath="$SRCDEST/$filename" local filetype=$(file -bizL "$filepath") # fix Arch flyspray #6246 local ext=${filename##*.} local cmd=bsdtar local ret=0 msg2 "$(gettext "Extracting %s with %s")" "$filename" "$cmd" rm -rf "$srcdir/$depname" mkdir "$srcdir/$depname" && $cmd -xf "$filepath" -C "$srcdir/$depname" --strip-components 1 || ret=$? if (( ret )); then error "$(gettext "Failed to extract %s")" "$file" plain "$(gettext "Aborting...")" exit 1 fi } download_git() { local netfile=$1 local repo="$(get_downloadname "$netfile")" local dir="$SRCDEST/$repo" local url="$(get_url "$netfile")" url=${url##git+} url=${url%%#*} if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "git" if ! git clone --mirror "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then pushd "$dir" &>/dev/null # Make sure we are fetching the right repo if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then error "$(gettext "%s is not a clone of %s")" "$dir" "$url" plain "$(gettext "Aborting...")" exit 1 fi msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "git" if ! git fetch --all -p; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "git" fi popd &>/dev/null fi } extract_git() { local netfile=$1 local url="$(get_url "$netfile")" local fragment= if [[ $url = *#* ]]; then fragment=${url#*#} fi local repo="$(get_downloadname "$netfile")" local src="$SRCDEST/$repo" local dst="$srcdir/$(get_depname "$netfile")" msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "git" rm -rf "${dst}" if ! git clone "$src" "$dst"; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi local ref= if [[ -n $fragment ]]; then case ${fragment%%=*} in commit|tag) ref=${fragment##*=} ;; branch) ref=origin/${fragment##*=} ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi if [[ -n $ref ]]; then pushd "${dst}" &>/dev/null if ! git checkout -b makepkg $ref; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi popd &>/dev/null fi } download_hg() { local netfile=$1 local repo=$(get_downloadname "$netfile") local dir="$SRCDEST/$repo" local url=$(get_url "$netfile") url=${url##hg+} url=${url%%#*} if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "hg" if ! hg clone -U "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "hg" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "hg" pushd "$dir" &>/dev/null if ! hg pull; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "hg" fi popd &>/dev/null fi } extract_hg() { local netfile=$1 local url="$(get_url "$netfile")" local fragment= if [[ $url = *#* ]]; then fragment=${url#*#} fi local repo="$(get_downloadname "$netfile")" local src="$SRCDEST/$repo" local dst="$srcdir/$(get_depname "$netfile")" msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "hg" rm -rf "${dst}" local ref= if [[ -n $fragment ]]; then case ${fragment%%=*} in branch|revision|tag) ref=('-u' "${fragment##*=}") ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi if ! hg clone "${ref[@]}" "$src" "$dst"; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "hg" plain "$(gettext "Aborting...")" exit 1 fi } download_svn() { local netfile=$1 local url="$(get_url "$netfile")" url=${url##svn+} local fragment= if [[ $url = *#* ]]; then fragment=${url#*#} fi url=${url%%#*} local repo=$(get_downloadname "$netfile") local dir="$SRCDEST/$repo" if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "svn" mkdir -p "$dir/.makepkg" if ! svn checkout --config-dir "$dir/.makepkg" "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "svn" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "svn" pushd "$dir" &>/dev/null if ! svn update; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "svn" fi popd &>/dev/null fi } extract_svn() { local netfile=$1 local url="$(get_url "$netfile")" url=${url##svn+} local fragment= if [[ $url = *#* ]]; then fragment=${url#*#} fi local repo="$(get_downloadname "$netfile")" local src="$SRCDEST/$repo" local dst="$srcdir/$(get_depname "$netfile")" msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "svn" rm -rf "${dst}" local ref= if [[ -n $fragment ]]; then case ${fragment%%=*} in revision) ref="${fragment##*=}" ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi cp -a "$src" "$dst" if [[ -n ${ref} ]]; then pushd "$dst" &>/dev/null if ! svn update -r ${ref}; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "svn" plain "$(gettext "Aborting...")" fi popd &>/dev/null fi } download_sources() { msg "$(gettext "Retrieving sources...")" local netfile for netfile in "$@"; do local proto=$(get_protocol "$netfile") case "$proto" in git) download_git "$netfile" extract_git "$netfile" ;; hg) download_hg "$netfile" extract_hg "$netfile" ;; svn) download_svn "$netfile" extract_svn "$netfile" ;; http|https) download_file "$netfile" extract_file "$netfile" ;; *) error "$(gettext "Unrecognized protocol: %s")" "$proto" return 1 ;; esac done } declare -r HOLDVER=0 declare -r srcdir="$PWD/src/$1"; shift declare -r SRCDEST="$PWD/src/downloads" declare -r DLAGENTS=('http::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' 'https::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u') mkdir -p "$srcdir" "$SRCDEST" download_sources "$@"