#!/usr/bin/env bash # (we use env for non-Linux OSes) # # Script to update/install the latest versions of all the most # important Common Lisp projects. Uses SBCL but otherwise tries to be # somewhat independent of your local environment. # # Intended to quickly bootstrap a working development environment for # Lisp free software hackers. # # Idea from jhbuild by James Henstridge (a Gnome hacker). # # Contributors: # Luke Gorrie # Anthony Chaumas-Pellet # Christophe Rhodes # David Lichteblau # Eric Marsden # Andreas Fuchs # Albert Krewinkel # Daniel White # Ryan Davis # Michael Weber # (and probably others) set -e if [ "$CLNET_USER" == "" ]; then CLNET_USER=:pserver:anonymous:anonymous fi export CLNET_USER # MacOS doesn't have "readlink -e", needed to follow symlinks. # We hack it using readlink, dirname, and cd. readlink_e() { self="$0" while test -h "$self"; do cd "$(dirname $self)" self=`readlink "$self"` done cd "$(dirname $self)" pwd } BASE=$(readlink_e) system_dir="$BASE/systems" source_dir="$BASE/source" target_dir="$BASE/target" if test x`uname -o 2>/dev/null` = xCygwin; then windowsp=1 else windowsp="" fi if test "$(uname -s)" = Darwin -a "$(uname -p)" = powerpc; then darwinppc=1 else darwinppc="" fi source "$BASE/clbuild.conf.default" if test -f "$BASE/clbuild.conf"; then source "$BASE/clbuild.conf" fi # All files which contain information about projects. Also include # personal projects listed in 'my-projects'. PROJECT_LISTING_FILES="$BASE/projects $BASE/wnpp-projects $BASE/implementations" if test -f "$BASE/my-projects"; then PROJECT_LISTING_FILES="$BASE/my-projects $PROJECT_LISTING_FILES" fi if test -f "$BASE/my-dependencies"; then DEPENDENCY_FILES="$BASE/dependencies $BASE/my-dependencies" else DEPENDENCY_FILES="$BASE/dependencies" fi # Fix up pathnames make_absolute_pn() { if [ -n "$1" ] ; then (cd "$BASE" echo "$(cd "$(dirname "$1")" ; pwd)/$(basename "$1")") fi } case $UPDATE_SCRIPT in update_project) # okay, new name ;; update.sh) # old name UPDATE_SCRIPT=update_project ;; *) # do will still want this? UPDATE_SCRIPT="$(make_absolute_pn "$UPDATE_SCRIPT")" ;; esac USER_INIT="$(make_absolute_pn "$USER_INIT")" if test -n "$windowsp" -a x$USER_INIT = x/dev/null; then USER_INIT=NUL fi # CLIM configuration case x$CLIM_BACKEND in xgraphic-forms|xgtkairo|xbeagle) EXTRA_CLIM_FEATURES="(pushnew :clim-$CLIM_BACKEND *features*)" maybe_load_clx="nil" ;; x|xclx) EXTRA_CLIM_FEATURES="nil" maybe_load_clx="(unless (find-package :xlib) (asdf:operate 'asdf:load-op :clx))" ;; *) echo "invalid $CLIM_BACKEND, see clbuild.conf.default for examples." 1>&2 exit 1 ;; esac if test -n "$windowsp"; then system_namestring="`cygpath -m $system_dir`/" source_namestring="`cygpath -m $source_dir`/" target_namestring="`cygpath -m $target_dir`/" self="c:/cygwin/bin/bash $self" else system_namestring="$system_dir/" source_namestring="$source_dir/" target_namestring="$target_dir/" fi if test -n "$SETF_CENTRAL_REGISTRY"; then set_central_registry="(setq asdf:*central-registry* '(#p\"${system_namestring}\"))" else set_central_registry="(push #p\"${system_namestring}\" asdf:*central-registry*)" fi set_lisp_warning() { echo "Warning: Cannot find an executable for implementation $1" 1>&2 } # Try to find an executable/command for a given lisp implementation set_lisp_command() { case "$1" in sbcl) if [ -x ${target_dir}/bin/sbcl ]; then export SBCL_HOME=${target_namestring}lib/sbcl/ export SBCL="${target_dir}/bin/sbcl" if ! test -f "$BASE"/monster.core; then SBCL="$SBCL --core ${target_namestring}lib/sbcl/sbcl.core" fi elif which sbcl >/dev/null ; then export SBCL="$(which sbcl)" else set_lisp_warning sbcl fi ;; alisp) if which alisp >/dev/null ; then export ALISP="$(which alisp)" else set_lisp_warning alisp fi ;; cmu) if which lisp >/dev/null ; then export CMU="$(which lisp)" else set_lisp_warning cmu fi ;; ecl) export DYLD_LIBRARY_PATH="${target_dir}/lib:$DYLD_LIBRARY_PATH" export ECL="${target_dir}/bin/ecl" ;; ccl) case `uname` in Linux) case `uname -m` in x86_64) binary=lx86cl64 ;; *) set_lisp_warning ccl exit 1 ;; esac ;; Darwin) case `uname -p` in powerpc) binary=dppccl ;; i386) binary=dx86cl64 ;; *) set_lisp_warning ccl exit 1 ;; esac ;; *) set_lisp_warning ccl ;; esac if [ -x ${source_dir}/ccl/${binary} ]; then export CCL=${source_dir}/ccl/${binary} else set_lisp_warning ccl exit 1 fi ;; clisp) if [ -x "${target_dir}/bin/clisp" ]; then export CLISP="${target_dir}/bin/clisp" else set_lisp_warning clisp exit 1 fi ;; ecl) if [ -x "${target_namestring}bin/ecl" ]; then export ECL="${target_namestring}bin/ecl" else set_lisp_warning ecl fi ;; *) set_lisp_warning "$1" ;; esac } configure_ccl() { if [ -z "$CCL" ]; then set_lisp_command ccl fi lisp=${1:-$CCL} noinform="-Q" end_toplevel_options="" #fixme quit="(ccl:quit)" eval="--eval" require_asdf="(require :asdf)" core_option="-I" if test x"$USER_INIT" = x/dev/null; then # -l /dev/null does not work common_options="-n" elif test -n "$USER_INIT"; then common_options="-n -l $USER_INIT" else common_options="" fi # fixme: this doesn't quite match the SBCL version yet: build_options="$noinform --batch $common_options" run_options="--batch $common_options" } configure_clisp() { if [ -z "$CLISP" ]; then set_lisp_command clisp fi lisp={$1:-$CLISP} echo '*** Using CLISP. Please note that CLISP support is not complete.' lisp="$CLISP -repl" noinform="" #fixme end_toplevel_options="" #fixme quit="(ext:quit)" eval="-x" require_asdf="(load \"$BASE/source/asdf-for-clisp/asdf.lisp\")" core_option="-M" if test -n "$USER_INIT"; then common_options="-norc -i $USER_INIT" else common_options="" fi build_options="-on-error exit $common_options" run_options="-on-error exit $common_options" if test -d "$BASE/source/asdf-for-clisp"; then echo "*** asdf checkout found" echo else echo "NEW checking out asdf for use with clisp" (cd "$BASE/source" && cvs -d ${SF_USER}@sbcl.cvs.sourceforge.net:/cvsroot/sbcl co -d asdf-for-clisp sbcl/contrib/asdf) fi } configure_alisp() { if [ -z "$ALISP" ]; then set_lisp_command alisp fi lisp={$1:-$ALISP} lisp="$ALISP" noinform="" #fixme end_toplevel_options="" #fixme quit="(excl:exit)" eval="-e" require_asdf="(require :sdf)" core_option="-I" if test -n "$USER_INIT"; then common_options="-qq -L $USER_INIT" else common_options="" fi # fixme build_options="$common_options" run_options="$common_options" } configure_sbcl() { if [ -z "$SBCL" ]; then set_lisp_command sbcl fi lisp=${1:-$SBCL} noinform="--noinform" end_toplevel_options="--end-toplevel-options" quit="(sb-ext:quit)" eval="--eval" require_asdf="(require :asdf)" core_option="--core" if test -n "$USER_INIT"; then common_options="--userinit $USER_INIT" else common_options="" fi build_options="$noinform --noprint --disable-debugger $common_options" run_options="--disable-debugger $common_options" } configure_cmu() { if [ -z "$CMU" ]; then set_lisp_command cmu fi lisp=${1:-$CMU} noinform="-quiet" end_toplevel_options="--" quit="(ext:quit)" eval="-eval" # require_asdf="(require :asdf)" require_asdf="(let* ((lisp \"$BASE/source/asdf/asdf.lisp\") (fasl (compile-file-pathname lisp))) (unless (probe-file fasl) (compile-file lisp)) (load fasl :verbose nil :print nil))" core_option="-core" if test x"$USER_INIT" = x/dev/null; then common_options="-noinit" elif test -n "$USER_INIT"; then common_options="-init $USER_INIT" else common_options="" fi build_options="$noinform -batch $common_options" run_options="-batch $common_options" if ! test -d "$BASE/source/asdf"; then (cd "$BASE/source" && cvs -d ${SF_USER}@sbcl.cvs.sourceforge.net:/cvsroot/sbcl co -d asdf sbcl/contrib/asdf) fi } # Allow --implementation system_sbcl configure_system_sbcl() { configure_sbcl lisp=$(which sbcl) } configure_ecl() { if [ -z "$ECL" ]; then set_lisp_command ecl fi lisp=${1:-$ECL} noinform="-q" end_toplevel_options="--" quit="(cl-user:quit)" eval="-eval" require_asdf="(require :asdf)" core_option="-c" if test -n "$USER_INIT" -a x"$USER_INIT" != x/dev/null; then common_options="-load $USER_INIT" else common_options="" fi build_options="$noinform $common_options" run_options="$common_options" } configure_lisp_implementation() { # Get the implementation type (member sbcl ccl clisp alisp ecl cmu) implementation=${1} # if the given parameter specifies a lisp implementation by name, the name # is used. Otherwise the DEFAULT_LISP_IMPLEMENTATION is used. Also check # for environment variables for backwards comparability. Defaults to SBCL. if [ -z ${implementation} ]; then if [ -n "${DEFAULT_LISP_IMPLEMENTATION}" ]; then implementation=${DEFAULT_LISP_IMPLEMENTATION} elif [ -n "${CCL}" ]; then implementation=ccl elif [ -n "${CLISP}" ]; then implementation=clisp elif [ -n "${ALISP}" ]; then implementation=alisp elif [ -n "${ECL}" ]; then implementation=ecl elif [ -n "${CMU}" ]; then implementation=cmu else implementation=sbcl fi fi if [ "$(type -t "configure_$implementation")" = function ] && \ "configure_$implementation"; then return else echo "Unknown implementation '$implementation'" 1>&2 exit 1 fi } export implementation='' if [ "$1" = "--implementation" ]; then implementation=$2 configure_lisp_implementation $implementation shift 2 else configure_lisp_implementation fi [ -d "$system_dir" ] || mkdir "$system_dir" [ -d "$source_dir" ] || mkdir "$source_dir" [ -d "$target_dir" ] || mkdir "$target_dir" clbuild_lisp() { ${lisp} $common_options \ $eval "$require_asdf" \ $eval "$set_central_registry" \ $eval "$EXTRA_CLIM_FEATURES" \ "$@" } lisp_trampoline() { # Start the Lisp with user arguments. For SBCL, we can do that using # --end-toplevel-options. For other Lisps, go through a temporary # file. options="$1" shift if test -n "$end_toplevel_options"; then ${lisp} \ $options \ $eval "$require_asdf" \ $eval "$set_central_registry" \ $eval "$EXTRA_CLIM_FEATURES" \ $eval "(load \"$BASE/clbuild.lisp\")" \ $end_toplevel_options \ "$@" else TMPDIR=`mktemp -d /tmp/clbuild.XXXXXXXXXX` export TMPDIR cleanup() { rm -rf $TMPDIR } trap cleanup EXIT while test -n "$1"; do # fixme: whitespacea echo $1 >>$TMPDIR/args shift done ${lisp} \ $options \ $eval "$require_asdf" \ $eval "$set_central_registry" \ $eval "$EXTRA_CLIM_FEATURES" \ $eval "(defparameter cl-user::*clbuild-args* \"$TMPDIR/args\")" \ $eval "(load \"$BASE/clbuild.lisp\")" fi } start_application() { lisp_trampoline "$noinform $run_options" "$@" } recompile() { if test x"$1" = x--dump; then dump="--dump t" shift else dump="" fi concatenated_args="$@" if test -z "$concatenated_args"; then concatenated_args="$main_projects" fi cd "$BASE" lisp_trampoline "$build_options" \ recompile-systems \ $dump \ "$concatenated_args" } count_systems() { n_asd=`ls -1 "$system_dir"/*.asd | wc -l` echo "$n_asd system definition files registered" } blank_line=" " tail_last() { if tty 0>&1 >/dev/null; then while read line; do echo -e '\r\c' echo -n "$blank_line" echo -e '\r\c' echo -n $line | cut -b 1-65 | tr -d '\n' done echo -e '\r\c' echo -n "$blank_line" echo -e '\r\c' else while read line; do echo $line done fi } dribble_get() { label="$1" name="$2" if [ -d $name ]; then echo -n "UPDATE " else echo -n "NEW " fi echo "$label $name" } darcs_record_import() { local name="$1" local url="$2" IMPORT_MESSAGE="Imported $name from $url on $(date)" darcs record -a -l -A clbuild -m "$IMPORT_MESSAGE" } dry_run_ok() { if test -n "$dry_run"; then echo "OK: $1" fi } dry_run_missing() { if test -n "$dry_run"; then echo "MISSING: $1" fi } get_hg() { local name="$1" local url="$2" if [ -d $name ]; then local actual=$(cd $name && hg showconfig paths.default) if [ "x$actual" = "x$url" ]; then dry_run_ok $name else echo "MISMATCH: $name was installed from $actual, current is $url" fi else dry_run_missing $name fi if [ -n "$dry_run" ]; then exit 0 fi if [ -d $name ]; then dribble_get "hg pull" $name ( cd $name if [ ! -d ".hg" ]; then echo "ERROR: not a mercurial repository" exit 1 fi hg pull --update ) else dribble_get "hg clone" $name hg clone $url $name fi } get_darcs() { name="$1" url="$2" if [ -d $name ]; then actual="`cat $name/_darcs/prefs/defaultrepo`" if test "x$actual" = "x$url"; then dry_run_ok $1 else echo "MISMATCH: $1 was installed from $actual, current is $url" fi else dry_run_missing $1 fi if test -n "$dry_run"; then exit 0 fi # don't use tail_last, since darcs already has this kind of progress bar if [ -d $name ]; then dribble_get "darcs pull" $name ( cd $name if ! test -d _darcs; then echo ERROR: not a darcs repository exit 1 fi darcs pull --all ) else dribble_get "darcs get" $name darcs get --lazy $url $name fi } get_git() { name="$1" url="$2" if [ -d $name ]; then actual="`cd $name && git config --get remote.origin.url`" if test "x$actual" = "x$url"; then dry_run_ok $1 else echo "MISMATCH: $1 was installed from $actual, current is $url" fi else dry_run_missing $1 fi if test -n "$dry_run"; then exit 0 fi if [ -d $name ]; then dribble_get "git pull" $name ( cd $name if ! test -d .git; then echo ERROR: not a git repository exit 1 fi git pull ) else dribble_get "git clone" $name git clone $url $name fi } get_svn() { name="$1" url="$2" if [ -d $name ]; then actual="`cd $name && svn info | grep ^URL: | awk '{print $2;}'`" if test "x$actual" = "x$url"; then dry_run_ok $1 else echo "MISMATCH: $1 was installed from $actual, current is $url" fi else dry_run_missing $1 fi if test -n "$dry_run"; then exit 0 fi dribble_get "svn co" $name svn co $url $name | tail_last } get_cvs_aux() { module="$1" repository="$2" target_directory="$3" if [ -d $module ]; then actual="`cat $module/CVS/Root`" if test "x$actual" = "x$repository"; then dry_run_ok $1 else echo "MISMATCH: $1 was installed from $actual, current is $repository" fi else dry_run_missing $1 fi if test -n "$dry_run"; then exit 0 fi dribble_get "cvs co" $module cvs -d $repository co ${3+-d "$3"} $module | tail_last } get_cvs_full() { get_cvs_aux $3 $2 $1 } get_tarball() { local name="$1" local url="$2" local flags="${3:-z}" if [ -d $name ]; then dry_run_ok $name else dry_run_missing $name fi if [ -n "$dry_run" ]; then exit 0 fi # if repository does not exist, then create and populate one if [ ! -d "$name/_darcs" ]; then ( darcs init --repodir=$name cd $name darcs_record_import $name $url ) fi # pull repository into temporary directory dribble_get "wget" $name ( local tmp="${name}.tar.gz" cd $TMPDIR # clone the original directory darcs get "${source_dir}/${name}" wget \ --no-check-certificate \ --progress=dot \ -O "$tmp" \ $url \ 2>&1 | tail_last tar v${flags}xf "$tmp" | tail_last rm $tmp # if directory names differ, copy into main directory local other_dir=$(echo ${name}?*/ | awk '{print $1}') if [ -d $other_dir ]; then cp -R ${other_dir}* $name fi ) # record any changes and pull back into original directory ( cd $TMPDIR/$name darcs_record_import $name $url darcs push -a -p "$IMPORT_MESSAGE" "$source_dir/$name" ) } get_svn_clnet() { name="$1" path="$2" get_svn $name svn://common-lisp.net/project/$name/svn/$2 } get_cvs_clnet() { module="$1" project="${2:-$1}" get_cvs_aux $module ${CLNET_USER}@common-lisp.net:/project/$project/cvsroot } get_cvs_clnet_full() { clbuildproject="$1" clnetproject="${2:-$1}" path="$3" get_cvs_aux $path ${CLNET_USER}@common-lisp.net:/project/$clnetproject/cvsroot $clbuildproject } get_cvs_sfnet() { module="$1" project="${2:-$1}" get_cvs_aux $module ${SF_USER}@$project.cvs.sourceforge.net:/cvsroot/$project } get_ediware() { get_darcs $1 http://common-lisp.net/~loliveira/ediware/$1 } get_clbuild_mirror() { get_darcs $1 http://common-lisp.net/project/clbuild/mirror/$1 } get_tarball_bz2() { get_tarball "$1" "$2" j } get_github() { project="$1" user="$2" repo="${3:-$1}" if test "$GITHUB_USER" = $user; then if test -n "$GITHUB_USE_HTTP"; then get_git $project https://$user@github.com/$user/$repo.git else get_git $project git@github.com:$user/$repo.git fi else if test -n "$GITHUB_USE_HTTP"; then get_git $project http://github.com/$user/$repo.git else get_git $project git://github.com/$user/$repo.git fi fi } update_project() { if test x$1 = x--dry-run; then shift dry_run=1 else unset dry_run fi export dry_run if test $# -ne 1; then exec 1>&2 echo error: invalid number of arguments echo usage: ... update [--dry-run] PROJECT_NAME exit 1 fi if ! grep -h "^$1 " $PROJECT_LISTING_FILES >/dev/null; then echo Error: cannot download unknown project $1 rm "$BASE/.clbuild-resume" exit 1 fi found=`grep -h "^$1 " $PROJECT_LISTING_FILES | cut -d\# -f1` update_project_2 $found } update_project_2() { name="$1" action="$2" shift shift ( $action $name "$@" ) } update() { if test -n "$clbuild_resume"; then if test $# -gt 0; then echo "error: --resume conflicts with arguments" 1>&2 exit 1 fi previous=`cat "$BASE/.clbuild-resume"` CLBUILD_DEPENDENCIES=no update_1 $previous elif test $# -gt 0; then rm -f $source_dir/*/.clbuild-skip-update update_1 $* else exec 1>&2 echo "Error: arguments expected" echo "Usage:" echo " clbuild update PROJECT1 PROJECT2 PROJECT3... # only these" echo " clbuild update --resume # resume interrupted update" echo " clbuild update --main-projects # all in projects file" echo " clbuild update --wnpp-projects # all in wnpp-projects file" echo " clbuild update --all-projects # both files" echo " clbuild update --installed # everything in source/" exit 1 fi } update_1() { touch "$BASE"/.core-is-stale TMPDIR=`mktemp -d /tmp/clbuild.XXXXXXXXXX` export TMPDIR cleanup() { if test -f "$BASE/.clbuild-resume"; then exec 1>&2 echo echo error: update was interrupted. echo 'Use "clbuild update --resume" to retry. (See also "clbuild skip PROJECT").' fi rm -rf $TMPDIR } trap cleanup EXIT cd "$source_dir" touch $TMPDIR/dependencies0 while test $# -ge 1; do echo $1 >>$TMPDIR/arguments0 if cat $DEPENDENCY_FILES | grep "^$1 ">/dev/null; then found=`cat $DEPENDENCY_FILES |grep "^$1 "` for x in $found; do echo $x >>$TMPDIR/dependencies0 done else echo $1 >>$TMPDIR/dependencies0 echo "warning: no dependencies for $1 found" 1>&2 fi shift done sort <$TMPDIR/arguments0 | uniq >$TMPDIR/arguments sort <$TMPDIR/dependencies0 | uniq >$TMPDIR/dependencies if ! cmp $TMPDIR/arguments $TMPDIR/dependencies >/dev/null; then case "$CLBUILD_DEPENDENCIES" in ask) extra=`diff $TMPDIR/arguments $TMPDIR/dependencies | grep '^>' | cut -d' ' -f2 | xargs echo` echo "The following extra dependencies were found: $extra" echo -n "include dependencies in update? (Y/n)" read reply case _"$reply" in _Y|_y|_) ;; _n) mv $TMPDIR/arguments $TMPDIR/dependencies ;; *) echo Invalid reply exit 1 ;; esac ;; yes) ;; no) mv $TMPDIR/arguments $TMPDIR/dependencies ;; *) echo "error: invalid \$CLBUILD_DEPENDENCIES" 1>&2 exit 1 esac fi cp $TMPDIR/dependencies "$BASE/.clbuild-resume" for project in $(cat $TMPDIR/dependencies); do skipfile="$project"/.clbuild-skip-update if test -f "$skipfile" -a -n "$clbuild_resume"; then echo "resume: skipping update of $project" else ${UPDATE_SCRIPT} ${UPDATE_ARGS} $project register_asd $project "quiet" touch "$skipfile" fi done link_extra_asds rm "$BASE/.clbuild-resume" echo "update complete" count_systems cd .. } show_project() { name=$1 unset match for f in $PROJECT_LISTING_FILES; do match=$(grep "^$name " $f || true) if test -n "$match"; then matching_file=$f break; fi done if test -z "$match"; then echo Error: no such project: $name exit 1 fi case $(basename $matching_file) in projects) comment="this is a main project" ;; wnpp-projects) comment="this is a work-needing or prospective project" ;; my-projects) comment="this is a local project" ;; esac cat <&2 exit 1 else echo Warning: Cannot find a working installation of "$1"! 1>&2 fi } check_program() { if ! "$1" --help 2>/dev/null >/dev/null; then check_error $1 $2 else echo "found `which $1`" fi } # for programs that don't understand --help, or (like cvs) are stupid enough # to return a failure code when invoked using a correct --help option... check_misdesigned_program() { if ! which "$1" 2>&1 >/dev/null; then check_error $1 $2 else echo "found `which $1`" fi } check() { MISSING_PROGRAMS="" echo "Checking for helper applications..." check_misdesigned_program cvs check_program svn check_program darcs check_program wget warn check_program hg warn # get_tarball is evil and unused anyway, so no need to bother the FreeBSD # users with warnings about their non-GNU version of tar # check_program tar check_misdesigned_program curl warn check_misdesigned_program git check_misdesigned_program mktemp if [ -z ${MISSING_PROGRAMS} ]; then echo "Success: All helper applications found." else echo "WARNING: Some projects can not be build without" \ "the following programs:" echo ${MISSING_PROGRAMS} fi echo echo "Checking Lisp startup..." if ! test -e "${lisp% *}"; then echo "Error: Don't have a Lisp system to bootstrap from." echo "Please install e.g. ${implementation} and try again." exit 1 elif ${lisp} $run_options $eval $quit >/dev/null; then echo "Success: Lisp starts up using \"$lisp\"" else echo "Error: Cannot run Lisp using \"$lisp\"" exit 1 fi echo echo "Looking for installable systems..." count_systems } dumpcore() { rm -f "$BASE"/.core-is-stale rm -f "$BASE"/monster.core recompile --dump "$@" } if test -f "$BASE"/monster.core; then # Initially I thought this warning might be helpful, but it's usually # false alarm: # if test -f "$BASE/.core-is-stale"; then # echo "using $BASE/monster.core" # echo 'note: "clbuild update" was used since core file creation' # echo 'note: consider re-running dumpcore' # fi run_options="$core_option "$BASE"/monster.core $run_options" fi scan_projects() { cat $1 | awk '{print $1;}' | while read name; do if test -n "$name" -a x"$name" != 'x#'; then echo -n "$name " fi done } scan_installed() { for f in ${source_dir}/*; do name=`basename "$f"` if test -d "$f"; then if test -d "$f"/_darcs \ -o -d "$f"/.git \ -o -d "$f"/.svn \ -o -d "$f"/CVS \ && grep "^$name " "$BASE/dependencies" >/dev/null then echo $name else echo "skipping $f" 1>&2 fi fi done } if test -f $BASE/my-projects; then my_projects=`scan_projects "$BASE/my-projects"` else my_projects="" fi main_projects=`scan_projects "$BASE/projects"` wnpp_projects=`scan_projects "$BASE/wnpp-projects"` all_projects="$main_projects $wnpp_projects $my_projects" implementations=`scan_projects "$BASE/implementations"` set_installed_projects() { installed_projects=`scan_installed` } list() { pattern="$1" TMPDIR=`mktemp -d /tmp/clbuild.XXXXXXXXXX` export TMPDIR cleanup() { rm -rf $TMPDIR } trap cleanup EXIT cat $PROJECT_LISTING_FILES | sort | grep -i -E "$pattern" | while read project rest; do if test -n "$project" -a x"$project" != 'x#'; then description=`echo $rest | cut -d\# -f2` case `cd $source_dir && ${UPDATE_SCRIPT} --dry-run $project | cut -d: -f1` in MISSING) status=u ;; MISMATCH) status="!" ;; OK) status="i" ;; *) echo "failed to check URL" 1>&2 ;; esac echo "$status $project" >>$TMPDIR/left echo $description >>$TMPDIR/right fi done paste $TMPDIR/left $TMPDIR/right | expand -t 25 exit 0 } ensure_clppcre() { if ! test -d "$source_dir/cl-ppcre"; then ( cd "$source_dir" update_project cl-ppcre ln -f -s $source_dir/cl-ppcre/*.asd "${system_dir}"/ ) fi } write_slime_configuration() { if test -n "$START_SLIME_USING_CORE"; then cmd=preloaded else cmd=lisp fi cat <$BASE/.swank-loader.lisp </dev/null; then echo "Warning: main project $project depends on non-main project $dependency." error=1 fi done done if test -n "$error"; then echo echo "Please demote each project listed above to wnpp-projects," echo "unless its dependency is known to be stable enough that" echo "it can be promoted to the main project list instead." echo echo "Alternatively, consider blacklisting parts of the projects" echo "that might be causing an unnecessary dependency of this" echo "kind." else echo ok fi } trash() { mkdir -p "$BASE/trash" basename=`basename $1` today=`date +'%Y-%m-%d'` trash="$BASE/trash/$today" if test -e "$trash"; then if test -e "$trash/$basename"; then trash=`mktemp -d $BASE/trash/${today}_${basename}_XXXXXXXXXX` fi else mkdir $trash fi echo moving "$1" to "$trash/$basename" mv "$1" "$trash" } clean_links() { cd $BASE/systems local quiet="$1" for link in $(find "$BASE/systems" -maxdepth 1 -type l); do local link_target=$(readlink "$link") if [ ! -e $link_target ]; then if [ -z $quiet ]; then echo "removing broken link from $link to $link_target" fi rm -- "$link" fi done } update_missing() { local CLBUILD_DEPENDENCIES=no for project in $1; do if ! test -d "${source_dir}/$project"; then update $project fi done } case $1 in check) check ;; list) list "${2:-.*}" ;; projects) cat <&2 echo error: invalid number of arguments echo usage: $0 show PROJECT_NAME exit 1 fi show_project $2 ;; mrproper) for d in $source_dir/* $target_dir; do if test -d "$d"; then trash "$d" else echo "skipping $d" fi done echo "deleting monster.core systems/*" rm -f "$BASE"/monster.core ${system_dir}/* ;; skip) touch "$source_dir/$2/.clbuild-skip-update" ;; update|install) unset clbuild_resume unset extra while test -n "$2"; do case "$2" in --no-dependencies) CLBUILD_DEPENDENCIES="no" shift ;; --dependencies) CLBUILD_DEPENDENCIES="yes" shift ;; --resume) clbuild_resume="yes" shift ;; --main-projects) extra="$extra $main_projects" shift ;; --wnpp-projects) extra="$extra $wnpp_projects" shift ;; --my-projects) extra="$extra $my_projects" shift ;; --all-projects) extra="$extra $all_projects" shift ;; --installed) set_installed_projects extra="$extra $installed_projects" shift ;; *) break ;; esac done export clbuild_resume export CLBUILD_DEPENDENCIES shift update "$@" $extra ;; update-missing) update_missing "${2:-$main_projects}" ;; recompile|dumpcore) operation="$1" shift unset projects while test -n "$1"; do case "$1" in --main-projects) projects="$projects $main_projects" shift ;; --wnpp-projects) projects="$projects $wnpp_projects" shift ;; --my-projects) projects="$projects $my-projects" shift ;; --all-projects) projects="$projects $all_projects" shift ;; --installed) CLBUILD_DEPENDENCIES="yes" set_installed_projects projects="$projects $installed_projects" shift ;; *) break ;; esac done if test -z "$projects" -a $# -eq 0; then exec 1>&2 echo "Error: arguments expected" echo "Usage:" echo " clbuild $operation PROJECT1 PROJECT2 PROJECT3... # only these" echo " clbuild $operation --main-projects # all in projects file" echo " clbuild $operation --wnpp-projects # all in wnpp-projects file" echo " clbuild $operation --my-projects # all in my-projects file" echo " clbuild $operation --all-projects # all three files" echo " clbuild $operation --installed # everything in source/" exit 1 fi ensure_clppcre case $operation in recompile) recompile "$@" $projects ;; dumpcore) dumpcore "$@" $projects ;; esac ;; updatesbcl) exec 1>&2 echo "error: invalid command updatesbcl" echo "Use 'clbuild update sbcl' instead." exit 1 ;; buildsbcl) exec 1>&2 echo "error: invalid command buildsbcl" echo "Use 'clbuild compile-implementation sbcl' instead." exit 1 ;; compile-implementation) case $2 in sbcl) if [ -n "$CCL" ]; then echo "Cowardly refusing to build SBCL when \$CCL is set." 1>&2 exit 1 fi if ! test -d ${source_dir}/sbcl; then echo "sbcl not found, try running 'clbuild update sbcl'" 1>&2 exit 1 fi # Enable threads if test -z "$windowsp" -a -z "$darwinppc"; then ctf=$source_dir/sbcl/customize-target-features.lisp if test -f $ctf; then echo $ctf already exists else echo creating $ctf cat >$ctf <&2 exit 1 fi if ! test -d ${source_dir}/ccl; then echo "ccl not found, try running 'clbuild update ccl'" 1>&2 exit 1 fi ccl_old=`dirname "$CCL"` ccl_new=${source_dir}/ccl case `uname` in Linux) case `uname -m` in x86_64) headerdir=x86-headers64 binary=lx86cl64 image=LX86CL64 ;; *) echo "don't know how to build CCL for your ISA, please adjust clbuild as needed" 2>&1 exit 1 ;; esac ;; Darwin) case `uname -p` in powerpc) headerdir=darwin-headers binary=dppccl image=dppccl.image ;; i386) headerdir=darwin-x86-headers64 binary=dx86cl64 image=dx86cl64.image ;; *) echo "don't know how to build CCL for your ISA, please adjust clbuild as needed" 2>&1 exit 1 ;; esac ;; *) echo "don't know how to build CCL for your OS, please adjust clbuild as needed" 2>&1 exit 1 ;; esac if ! test -f "$ccl_old/$image"; then echo "can't find existing CCL installation in $ccl_old" 2>&1 exit 1 fi # cp $ccl_old/$headerdir/libc/*.cdb $ccl_new/$headerdir/libc/ cp -r $ccl_old/$headerdir $ccl_new/ || true cp $ccl_old/$binary $ccl_old/$image $ccl_new/ cd $ccl_new chmod +x $binary ./$binary -e '(rebuild-ccl :force t)' cat <&2 exit 1 fi cd $source_dir/clisp #./autogen.sh ulimit -s 16384 rm -f src/config.cache ./configure --with-libsigsegv-prefix=${LIBSIGSEGV_PREFIX} --prefix=$target_dir cd src make make install ;; ecl) cd $source_dir/ecl # If we don't clean the directory first, we get strange errors # on some mac systems, telling that there is no applicable # type for cl-fixnum.. make distclean || make clean || true ./configure --enable-slow-config --with-system-gmp=no \ --prefix=${target_dir} make make install ;; *) echo "unrecognized argument. valid are: sbcl ccl clisp" 2>&1 exit 1 ;; esac ;; lisp) shift; clbuild_lisp "$@" ;; prepl) shift; if test -f "$BASE"/monster.core; then common_options="$core_option "$BASE"/monster.core $common_options" fi clbuild_lisp "$@" \ $eval "(asdf:operate 'asdf:load-op :hemlock.tty)" \ $eval "(hemlock:repl)" ;; preloaded) shift; if test -f "$BASE"/monster.core; then common_options="$core_option "$BASE"/monster.core $common_options" fi clbuild_lisp "$@" ;; slime) shift emacs_args="$@" emacs=${EMACS-emacs} write_slime_configuration >"$BASE/.start-slime.el" $emacs -l "$BASE/.start-slime.el" ${emacs_args} ;; slime-configuration) shift echo ';; add this to your ~/.emacs to use clbuild and its slime:' echo ';;' write_slime_configuration ;; make-project) if test $# -ne 2; then exec 1>&2 echo error: invalid number of arguments echo usage: $0 create-new-project NEW_PROJECT_NAME exit 1 fi project="$2" if echo "$project" | grep '[ :/\\"'\'']' >/dev/null; then exec 1>&2 echo error: invalid project name exit 1 fi for existing in $all_projects; do if test x"$existing" = x"$project"; then exec 1>&2 echo error: project name already known: "$project" exit 1 fi done if test -f "$source_dir/$project"; then exec 1>&2 echo error: "$source_dir/$project" already exists exit 1 fi mkdir "$source_dir/$project" cd "$source_dir/$project" cat >>$project.asd <>package.lisp <>$project.lisp <>$project.sh <&2 echo error: invalid number of arguments echo usage: clbuild run any SYSTEM FORM exit 1 fi system="$2" form="$3" common_options="$noinform $common_options" if test -f "$BASE"/monster.core; then common_options="$core_option "$BASE"/monster.core $common_options" fi clbuild_lisp \ $eval "(asdf:operate 'asdf:load-op :$system)" \ $eval "$form" \ $eval "$quit" exit $? ;; climplayer) check_program fileschanged check_misdesigned_program mplayer ;; hunchentoot|webdav) if ! test -f /usr/lib/libssl.so; then echo "WARNING: /usr/lib/libssl.so not found, did you install OpenSSL?" echo "(type RET to continue anyway)" read fi ;; perfectstorm) if ! test -f /usr/include/GL/glut.h; then echo "WARNING: /usr/include/GL/glut.h not found, did you install OpenGL libraries?" echo "(type RET to continue anyway)" read fi ;; hemlock-slave) # override --disable-debugger etc. for this command run_options="$common_options" ;; esac start_application "$@" ;; record-dependencies) cd "$BASE" update_missing "$all_projects" # hack: don't want the core here run_options="$build_options" start_application record-dependencies \ "$main_projects $wnpp_projects" \ dependencies start_application record-dependencies \ "$my_projects" \ my-dependencies.new wnpp_check if ! test -f my-dependencies; then mv my-dependencies.new my-dependencies elif ! cmp my-dependencies.new my-dependencies >/dev/null; then mv -i my-dependencies.new my-dependencies fi ;; check-urls) cd "$source_dir" for project in ${2:-$all_projects}; do ${UPDATE_SCRIPT} --dry-run $project done ;; clean-links) clean_links ;; rebuild-links) rm -f ${system_dir}/* for project in ${all_projects}; do register_asd $project done link_extra_asds count_systems ;; register-asd) project="$2" dir="$source_dir/$project" if test -z "$project"; then echo "usage: clbuild register-asd PROJECT" 1>&2 exit 1 fi if ! test -d "$dir"; then echo "cannot find $dir" 1>&2 exit 1 fi register_asd $project link_extra_asds count_systems ;; pwd) echo $BASE ;; wnpp-check) wnpp_check ;; diff) diff="${source_dir}/.diff" cp /dev/null "$diff" cd "${source_dir}" darcs diff -u >>"$diff" set +e for f in *; do g="${source_dir}/$f" if test -d "$g"; then cd "$g" echo -n diffing $f... 1>&2 if test -d CVS; then cvs diff -u 2>/dev/null | grep -v '^?' >>"$diff" elif test -d .svn; then svn diff >>"$diff" elif test -d '{arch}'; then baz diff >>"$diff" elif test -d _darcs; then darcs diff -u >>"$diff" elif test -d .git; then git diff >>"$diff" else echo -n " FAILED" 1>&2 fi echo 1>&2 fi done less "$diff" ;; --long-help) case $2 in run) help_run ;; *) long_help esac ;; help|-H|""|--help|-h) case $2 in run) help_run ;; *) short_help esac ;; *) echo "invalid command $1, try --help for help" exit 1 esac