#! /bin/sh ################################################################################ # Script for checking out and updating Haskell packages via darcs ################################################################################ set -e scriptname=$0 package_list="package-list" hackage_archive="http://hackage.haskell.org/packages/archive" ################################################################################ # Output an informative message if we are in verbose mode. ################################################################################ message () { if test ${verbose} = "yes"; then echo "== $*" >&2 fi } ################################################################################ # Print an error message on stderr and terminate script with an error code. ################################################################################ die () { echo "${scriptname}: $1" >&2 exit 1 } ################################################################################ # Run an external command if we are not in dry-run mode. ################################################################################ run_command() { message "Running \"$*\"" if test ${dry_run} = "no"; then "$@" fi } ################################################################################ # Higher order shell programming: Map a function over all packages. ################################################################################ map_packages() { func="$*" while read url kind version; do # Ignore empty lines and comment lines. if test -z "${url}"; then continue fi case ${url} in \#*) continue ;; */*) package_url_prefix=`echo ${url} | sed 's,/[^/]*$,,'` package=`echo ${url} | sed 's,.*/,,'` ;; *) package_url_prefix="" package=${url} ;; esac # Sanity check if test -z "${kind}" -o -z "${version}"; then die "Missing kind/version in package list." fi ${func} "${package_url_prefix}" "${package}" "${kind}" "${version}" done < "${package_list}" } ################################################################################ # Handle command line arguments. ################################################################################ extra="no" nofib="no" testsuite="no" release="no" dry_run="no" list_packages="no" verbose="no" while test $# -gt 0; do case $1 in -e | --extra) extra="yes" ;; -n | --nofib) nofib="yes" ;; -t | --testsuite) testsuite="yes" ;; -r | --release) release="yes" ;; -d | --dry-run) dry_run="yes" ;; -l | --list-packages) list_packages="yes" ;; -v | --verbose) verbose="yes" ;; -*) die "unknown flag \"$1\"" ;; *) break ;; esac shift done ################################################################################ # Make sure we are called in the correct place, i.e. the top level. ################################################################################ if test ! -d "_darcs" -o ! -f ${package_list}; then die "run \"$0\" in the top level directory of the project" fi message "We are at the top level of a darcs project." ################################################################################ # If requested, list package names and exit. ################################################################################ # I want anonymous shell functions! Join the petition! echo_package() { package_url_prefix=$1; package=$2; kind=$3; version=$4 if test "${kind}" = "core" -o ${extra} = "yes"; then echo "${package}" fi } if test ${list_packages} = "yes"; then map_packages echo_package exit fi ################################################################################ # Automagically determine our Haskell implementation via .spec files. ################################################################################ implementation="unknown" for i in *.spec *.spec.in; do if test -f ${i}; then implementation=`echo ${i} | sed 's/\.spec$//' | sed 's/\.spec\.in$//'` break fi done if test ${implementation} = "unknown"; then die "can't determine Haskell implementation" fi message "It looks like we are at the top level of the \"${implementation}\" project." ################################################################################ # Creativity is not *always* a good thing: Figure out where the individual tools # and packages should live. ################################################################################ case ${implementation} in ghc) tools_dir="." packages_dir="libraries" ;; hugs98) tools_dir="." packages_dir="packages" ;; nhc98) tools_dir="src" packages_dir="src/libraries" ;; *) die "unknon implementation \"${implementation}\"" ;; esac message "Tools will live in \"${tools_dir}\"." if test -d ${packages_dir}; then message "Packages will live in \"${packages_dir}\"." else run_command mkdir "${packages_dir}" message "Packages will live in \"${packages_dir}\" (created)." fi ################################################################################ # Figure out where to get the other repositories from, based on where this repo # itself came from. ################################################################################ defaultrepo=`cat _darcs/prefs/defaultrepo` case ${defaultrepo} in http://* | *@*:*) repository_base=`echo ${defaultrepo} | sed 's!/[^/]*$!!'` lib_repository_base="${repository_base}/packages" ;; /*) repository_base=${defaultrepo} lib_repository_base="${defaultrepo}/libraries" ;; *) die "malformed default repository \"${defaultrepo}\"" ;; esac message "Repository base is \"${repository_base}\"." message "Libaries repository base is \"${lib_repository_base}\"." ################################################################################ # Normalize darcs command name. ################################################################################ if test $# -eq 0; then die "missing darcs command" fi case $1 in g | ge | get) darcs_command="get" ;; w | wh | wha | what | whats | whatsn | whatsne | whatsnew) darcs_command="whatsnew" ;; *) darcs_command=$1 ;; esac shift message "The darcs command is \"${darcs_command}\"." ################################################################################ # To avoid pulling in a decade of patches, we make partial repositories the # default, but the user can override this, of course. ################################################################################ case $* in *--complete* | *--partial*) additional_get_option="" ;; *) message "Adding \"--partial\" to darcs commands." additional_get_option="--partial" ;; esac ################################################################################ # If we are going to retrieve a release version, we need either curl or wget. # Try to figure out which one via some ugly autoconf-like shell horror. ################################################################################ if test "${darcs_command}" = "get" -a ${release} = "yes"; then path_separator=":" test_x_works="no" # In a dry run, we simply make assumptions, avoiding the creation of a script. if test ${dry_run} = "yes"; then message "Note: In a dry run we assume that ':' is the path separator and 'test' does not support '-x'" else # For the tests below, we temporarily need an executable file in the current # directory, which returns successfully. test_script="darcs-all$$.sh" cat > ${test_script} << EOF #! /bin/sh exit 0 EOF chmod +x ${test_script} # The *nix vs. Windows war continues... ( PATH="/nonexistentsillydir;."; ${test_script} ) > /dev/null 2>&1 && path_separator=';' # The -x option of 'test' is not available everywhere, but it is not strictly # needed later, anyway. test -x ${test_script} > /dev/null 2>&1 && test_x_works="yes" rm -f ${test_script} fi # Walk through the PATH to find either curl or wget. downloader="" for prog in curl wget; do saved_IFS=$IFS IFS=$path_separator for dir in $PATH; do IFS=$saved_IFS test -z "${dir}" && dir="." if test -f "${dir}/${prog}"; then test ${test_x_works} = "yes" && test ! -x "${dir}/${prog}" && continue downloader=${prog} break fi done IFS=$saved_IFS test -n "${downloader}" && break done if test -z "${downloader}" -a ${dry_run} = "yes"; then message "Neither \"curl\" nor \"wget\" could be found in the PATH, assuming \"curl\"." downloader="curl" fi case "x${downloader}" in x) die "Neither \"curl\" nor \"wget\" could be found in the PATH." ;; xcurl) download_command="curl" ;; xwget) download_command="wget --output-document=-" ;; esac message "We will use \"${download_command}\" to retrieve released packages." fi ################################################################################ # Process ourselves and any tools. ################################################################################ tools="" # We don't need to get ourselves again. if test "${darcs_command}" != "get"; then tools=". ${tools}" fi case ${implementation} in ghc) if test "${darcs_command}" = "get"; then if test ${nofib} = "yes"; then tools="${tools} nofib"; fi if test ${testsuite} = "yes"; then tools="${tools} testsuite"; fi else if test -d "${tools_dir}/nofib"; then tools="${tools} nofib"; fi if test -d "${tools_dir}/testsuite"; then tools="${tools} testsuite"; fi fi ;; hugs98 | nhc98) tools="${tools} cpphs hsc2hs" ;; esac for tool in ${tools}; do if test "${darcs_command}" = "get"; then if test -d "${tools_dir}/${tool}"; then message "The tool \"${tool}\" already exists, skipping." continue fi run_command darcs "${darcs_command}" ${additional_get_option} "$@" --repodir "${tools_dir}/${tool}" "${repository_base}/${tool}" else if test ! -d "${tools_dir}/${tool}/_darcs"; then message "The directory \"${tools_dir}/${tool}\" is not present or not a repository, skipping." continue fi run_command darcs "${darcs_command}" "$@" --repodir "${tools_dir}/${tool}" fi done ################################################################################ # Process packages using the package list. ################################################################################ user_darcs_options="$*" retrieve_package() { package_url_prefix=$1; package=$2; kind=$3; version=$4 # We need to handle the "get" command differently, mostly due to the "release" # mode, where we use curl/wget to retrieve a specific version. if test "${darcs_command}" = "get"; then if test "${kind}" = "extra" -a ${extra} = "no"; then message "Package \"${package}\" is not a core package, skipping." continue fi if test -d "${packages_dir}/${package}"; then message "Package \"${package}\" already exists, skipping." continue fi if test ${release} = "yes"; then # Retrieve a numbered version from Hackage via curl/wget. run_command ${download_command} "${hackage_archive}/${package}/${package}-${version}.tar.gz" | \ ( if test ${dry_run} = "yes"; then cat; else gunzip -c | ( cd ${packages_dir}; tar xf - ); fi ) run_command mv "${packages_dir}/${package}-${version}" "${packages_dir}/${package}" else # Retrieve a bleeding edge version from the same repository as we came # from or from a given URL. if test -z "${package_url_prefix}"; then package_url_prefix=${lib_repository_base} fi run_command darcs "${darcs_command}" ${additional_get_option} ${user_darcs_options} --repodir "${packages_dir}/${package}" "${package_url_prefix}/${package}" fi else # This is not a "get" command, so proceed as usual. if test ! -d "${packages_dir}/${package}/_darcs"; then message "The directory \"${packages_dir}/${package}\" is not present or not a repository, skipping." continue fi message "Processing \"${packages_dir}/${package}\"" run_command darcs "${darcs_command}" ${user_darcs_options} --repodir "${packages_dir}/${package}" fi } map_packages retrieve_package