#!/bin/bash # # Copyright 2004-2005 The Apache Software Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # The command line interface to the common gump tasks. # ############################################################################## # Documentation ############################################################################## # Documentation for the different commands. function usage { case $1 in generate-reference) shift echo "= Command line options reference =" echo "{{{" usage help $@ echo "}}} == Environment variables == {{{" usage variables $@ echo "}}} == The 'run' command == {{{" usage run $@ echo "}}} == The 'dynagump' command == {{{" usage dynagump $@ echo "}}} == The 'debug' command == {{{" usage debug $@ echo "}}} == The 'debug-with-wing' command == {{{" usage debug-with-wing $@ echo "}}} == The 'test' command == {{{" usage test $@ echo "}}} == The 'pydoc' command == {{{" usage pydoc $@ echo "}}}" local currentdate=`date` echo " " echo " " echo "This document was last generated on $currentdate." echo "It can be generated using the command 'gump help generate-reference'" ;; variables) echo " Gump needs various other programs available in order to run. You can change which programs gump tries to use using environment variables. In addition, several core gump settings are also customizable using environment variables. You can set all these variables (except for GUMP_HOME) in the file $GUMP_ENV_FILE the location of this file is found as follows: GUMP_HOME/GUMP_HOSTNAME-settings.sh Recognized variables are: GUMP_HOME -- location of the gump subversion checkout. Defaults to the current working directory if possible. GUMP_HOSTNAME -- name of this machine. Defaults to the output from the hostname command. GUMP_ENV_FILE -- location of the file that contains the custom settings to load (i.e. the file mentioned above). You can override GUMP_HOME and GUMP_HOSTNAME here, but that may have some unpredictable effects. GUMP_PYTHON -- the name of the python executable to use. Defaults to the latest version of python that is installed. Note that pygump is supported only on python2.4. GUMP_WORKDIR -- the directory that pygump will generate various files in (like log output). Defaults to GUMP_HOME/pygump/work. JAVA_HOME -- the location of a java development kit. Gump tries to work with any JDK, but results may vary (for example, both ant and maven require jdk 1.2 at least). These variables are only used by dynagump: JAVA_OPTIONS -- Extra options to pass to the JVM. JETTY_PORT -- Override the default port for Jetty. Defaults to 8080. JETTY_ADMIN_PORT -- The port where the jetty web administration should bind. Defaults to 8081. JAVA_DEBUG_PORT -- The port the JVM debug server should listen to. Defaults to 8082. Of course, the various commands that gump issues may also behave differently based on environment variables. For example, maven reacts to MAVEN_HOME, many make-based build scripts respect the CC environment variable, etc etc. " ;; run) echo " Run pygump. " run -h ;; kill) echo " Prematurely end a pygump run. Usage: $0 kill " ;; debug) echo " Run pygump in debug mode. Usage: $0 debug [gump.py-args ...] This is not the same as executing the 'run' command with a '--debug' parameter. Using this command will actually start the command line debugger pdb to run gump in, whereas the '--debug' option customizes the log verbosity gump will use. This command otherwise accepts the same arguments as the 'run' command. " ;; debug-with-wing) echo " Run pygump in debug mode. Usage: $0 debug [gump.py-args ...] This is not the same as executing the 'run' command with a '--debug' parameter. Using this command will actually start the debug connector for the Wing IDE and attach it to the gump process, whereas the '--debug' option customizes the log verbosity gump will use. This command otherwise accepts the same arguments as the 'run' command. " ;; test) echo " Run pygump its unit tests. Usage: $0 test [OPTIONS] Available options include:" # This is a little wacky way to get just the option list from the # pylid.py script. It works for now :-D python $GUMP_HOME/bin/pylid-0.3/pylid.py -h 2>&1 | grep ' ' ;; dynagump) echo " Run Dynagump. Usage: $0 dynagump dynagump-action [dynagump-args ...] The available actions are: " dynagump help | grep -v '^Usage' | grep -v '^actions' echo " If no action is specified, gump passes run as the action to execute." ;; webgump) echo " Run Webgump. Usage: $0 webgump webgump-action [webgump-args ...] The available actions are: " ls -1 $GUMP_HOME/webgump/bin echo " If no action is specified, gump passes debug as the action to execute." ;; update-host) echo " Updates the gump installation on the specified host. Usage: $0 update-host hostname [hostname-script-args ...] Some gump hosts are managed by keeping some of their config data in our subversion repository, under the $GUMP_HOME/hosts/ directory. This command will attempt to ssh into the specified host machine and perform the neccessary svn update commands to bring the host in sync with the material in the subversion repository. A custom script named $GUMP_HOME/hosts/hostname-update.sh is ran for each host. See each of these scripts for information on the custom settings available with a particular host. " ;; create-database) echo " Create a new MySQL database with the dynagump database schema. Usage: $0 create-database [database-name] [mysql-args ...] If no database-name is specified, gump will assume the database should be named "gump". All other provided arguments are passed on to mysql, so if you need to connect using a different username and password you can do so, for example $0 create-database gump -u gump -p password_for_gump_user This command will fail if the specifed database already exists. " ;; pydoc) echo " Runs a pydoc server on port 1234. Usage: $0 pydoc Visit http://localhost:1234/ to see the documentation it provides " ;; pycompile) echo " Compiles all pygump source files. Usage: $0 pycompile Please run this before committing changed sourcefiles, to make sure they at least compile. " ;; new-unit-test) echo " Generates a new unit test. Usage: $0 new-unit-test -m module.to.test [-n nameoftest] " ;; *) echo " Utility commandline interface for Gump. Usage: $0 command [opts ...] Available commands are: run -- run pygump debug -- run pygump in debug mode, attaching pdb debug-with-wing -- run pygump in debug mode, attaching the Wing IDE test -- run the pygump unit tests dynagump -- run the dynagump web application server webgump -- run the webgump application server update-host -- update the configuration of a gump host create-database -- create a new gump MySQL database pycompile -- compile all pyump source files Run $0 help [command] for more information about a particular command. Run $0 help variables for more information about the environment variables that alter gump its behaviour. " ;; esac } ############################################################################## # Setup methods ############################################################################## # All of these are guaranteed to have run before any other method is # called, so other methods may safely assume that the basic environment # these methods put in place is there. # Determine the short form of the hostname function find_hostname { if [[ ! -z "$GUMP_HOSTNAME" ]]; then return fi local hostnamecommand=`which hostname` if [[ -z "$hostnamecommand" || "$hostnamecommand" == "no*" ]]; then error "GUMP_HOSTNAME is not set and the hostname command is not available to determine it!" fi local no_s_flag="no"; case "`uname`" in CYGWIN*) no_s_flag="yes" ;; Sun*) no_s_flag="yes" ;; esac if [[ "x$no_s_flag" == "xyes" ]]; then export GUMP_HOSTNAME=`hostname` else export GUMP_HOSTNAME=`hostname -s` fi } # Determine the location gump is installed in function find_home { if [[ ! -z "$GUMP_HOME" ]]; then return fi # look for this file local current=`pwd` local thisscript="`pwd`/gump" if [[ -f "$thisscript" ]]; then # guess... export GUMP_HOME="$current" return fi # look for the path to this file local homedir=`dirname $0` local thisscript="$homedir/gump" if [[ -f "$thisscript" ]]; then # guess... export GUMP_HOME="$homedir" return fi # not found - complain! error "GUMP_HOME is not set and failed to determine it. Please set it to the root of your svn checkout!" } # Determine the location of the work directory function find_workdir { if [[ ! -z "$GUMP_WORKDIR" ]]; then return fi export GUMP_WORKDIR="$GUMP_HOME/pygump/work" if [[ ! -d $GUMP_WORKDIR ]]; then mkdir "$GUMP_WORKDIR" fi } # Determine the location of the environment variables script function find_env_file { export GUMP_ENV_FILE="$GUMP_HOME/$GUMP_HOSTNAME-settings.sh" } # Determine whether we are running under cygwin function find_cygwin { local cygwin="no"; case "`uname`" in CYGWIN*) cygwin="yes" ;; esac if [[ "x$cygwin" == "xyes" ]]; then export GUMP_CYGWIN="yes" fi } # Determine the python command to use function find_python { if [[ ! -z "$GUMP_PYTHON" ]]; then return fi local python=`which python2.4 2>/dev/null` if [[ -z "$python" || "$python" == "no"* ]]; then python=`which python2.3 2>/dev/null` fi if [[ -z "$python" || "$python" == "no"* ]]; then python=`which python2.2 2>/dev/null` fi if [[ -z "$python" || "$python" == "no"* ]]; then python=`which python2 2>/dev/null` fi if [[ -z "$python" || "$python" == "no"* ]]; then python=`which python 2>/dev/null` fi if [[ -z "$python" || "$python" == "no"* ]]; then python="python" # hope for the best... fi export GUMP_PYTHON="$python" } # Load the environment variables script if it exists, then fill out missing # but required variables function setup_env { find_hostname find_home find_env_file if [[ -f "$GUMP_ENV_FILE" ]]; then . "$GUMP_ENV_FILE" fi find_cygwin find_python find_workdir } ############################################################################## # Utility functions ############################################################################## # These functions don't represent actual commands but rather are utilized # from the command functions. # Print an error message and then exit. function error { echo "$0: Fatal error! $1" exit 1 } # Print a friendly error message if some dependency is missing. # # Arguments: # - name of the command # - url to download the package that provides it function check_command { local cmd=`$GUMP_HOME/bin/PrintPath $1`; if [[ -z "$cmd" && ! -x "$1" ]]; then error "Cannot find $1. Please retrieve it from $2 and install it. If it is already installed, modify your \$PATH variable to point to it. You can customize the \$PATH variable inside a file named $GUMP_ENV_FILE if you wish." fi } # Print a friendly error message if an environment variable is not set. # # Arguments: # - name of the variable # - description of what the variable should be set to function check_env_var { local dereferenced="${!1}" if [[ -z "$dereferenced" ]]; then error "The variable $1 has not been set. It should be set to $2. You can either set this before invoking gump, or set it in a file named $GUMP_ENV_FILE if you wish." fi } # Print a friendly error message i a python library is not available. # # Arguments: # - the library to import # - url to download location of the library function check_pylib { cat > "$GUMP_HOME/pycmd.tmp.py" < $pidfile } # remove our .pid file function unlock { local pidfile="$GUMP_HOME/pygump/pygump.pid" if [[ ! -f "$pidfile" ]]; then error "Pygump lock file $pidfile not found!" fi local pid=`cat $pidfile` if [[ "$$" != "$pid" ]]; then error "Pygump lockfile pid is $pid but we're $$!" fi rm -f $pidfile } ############################################################################# # Action functions ############################################################################# # There is one of these for each command that gump understands. Stricly # speaking, the usage function above is also an action function, but we put # it at the top of the file for readability :-D # compile all of pygump function pycompile { # get rid of old stuff find $GUMP_HOME/pygump -type f -name '*.pyc' | xargs rm -Rf # compile everything if [[ ! -z "$GUMP_CYGWIN" ]]; then $GUMP_PYTHON -c "import re; import os; from compileall import compile_dir; compile_dir(os.path.join('`cygpath -w $GUMP_HOME`', 'pygump', 'python'), 10, '`cygpath -w $GUMP_HOME`', False, re.compile('\.svn'), True)" else $GUMP_PYTHON -c "import re; import os; from compileall import compile_dir; compile_dir(os.path.join('$GUMP_HOME', 'pygump', 'python'), 10, '$GUMP_HOME', False, re.compile('\.svn'), True)" fi # add pygump to python path local oldpythonpath="$PYTHONPATH" if [[ -z "$oldpythonpath" ]]; then if [[ ! -z "$GUMP_CYGWIN" ]]; then export PYTHONPATH="`cygpath -w $GUMP_HOME/pygump/python`;`cygpath -w $GUMP_HOME/pygump`" else export PYTHONPATH="$GUMP_HOME/pygump/python:$GUMP_HOME/pygump" fi else if [[ ! -z "$GUMP_CYGWIN" ]]; then export PYTHONPATH="`cygpath -w $GUMP_HOME/pygump/python`;`cygpath -w $GUMP_HOME/pygump`:$PYTHONPATH" else export PYTHONPATH="$GUMP_HOME/pygump/python:$GUMP_HOME/pygump:$PYTHONPATH" fi fi # import all modules, one by one, to check syntax local current=`pwd` cd $GUMP_HOME/pygump local files=`find . -name '*.py' -not -path './work*' | sed -e 's/\.\///g' -e 's/\//./g' -e 's/\.py//g' -e 's/python\.//g' | grep -v __init__` for i in $files; do $GUMP_PYTHON -c "import $i; import sys; sys.exit()" done cd $current # restore python path if [[ ! -z "$oldpythonpath" ]]; then export PYTHONPATH="$oldpythonpath" fi } # Run pygump using the specified python command function do_run { # add pygump to python path local oldpythonpath="$PYTHONPATH" if [[ -z "$oldpythonpath" ]]; then if [[ ! -z "$GUMP_CYGWIN" ]]; then export PYTHONPATH="`cygpath -w $GUMP_HOME/pygump/python`;`cygpath -w $GUMP_HOME/pygump`" else export PYTHONPATH="$GUMP_HOME/pygump/python:$GUMP_HOME/pygump" fi else if [[ ! -z "$GUMP_CYGWIN" ]]; then export PYTHONPATH="`cygpath -w $GUMP_HOME/pygump/python`;`cygpath -w $GUMP_HOME/pygump`:$PYTHONPATH" else export PYTHONPATH="$GUMP_HOME/pygump/python:$GUMP_HOME/pygump:$PYTHONPATH" fi fi local module="$1" shift # run pygump local current=`pwd` # disabled for speedier runs # pycompile if [[ ! -z "$GUMP_CYGWIN" ]]; then local old_gump_home=$GUMP_HOME local old_gump_workdir=$GUMP_WORKDIR local old_java_home=$JAVA_HOME export GUMP_HOME=`cygpath -w $GUMP_HOME` export GUMP_WORKDIR=`cygpath -w $GUMP_WORKDIR` export JAVA_HOME=`cygpath -w $JAVA_HOME` cd "$GUMP_HOME\pygump" else cd "$GUMP_HOME/pygump" fi $GUMP_PYTHON -m "$module" $@ if [[ ! -z "$GUMP_CYGWIN" ]]; then export GUMP_HOME=$old_gump_home export GUMP_WORKDIR=$old_gump_workdir export JAVA_HOME=$old_java_home fi cd "$current" # restore python path if [[ ! -z "$oldpythonpath" ]]; then export PYTHONPATH="$oldpythonpath" fi } # Run pygump in normal mode function run { lock do_run main $@ unlock } # Run pygump in the debugger function debug { lock run --attach-pdb $@ unlock } # Run pygump in the Wing IDE debugger function debug_with_wing { lock run --attach-wingdb $@ unlock } # Run pydoc on the pygump codebase function pydoc { do_run "pydoc" -p 1234 gump $@ } # Prematurely shut down pygump function do_kill { local pidfile="$GUMP_HOME/pygump/pygump.pid" if [[ ! -f "$pidfile" ]]; then error "Pygump lock file $pidfile not found, no process to kill!" fi local pid=`cat $pidfile` rm $pidfile local findpid=`ps -o pid -p $pid | egrep '[0-9]+' | sed -e 's/ //g'` if [[ -z "$findpid" ]]; then error "Process ID specified in Pygump lockfile not found, no process to kill!" fi kill $pid local findpid=`ps -o pid -p $pid | egrep '[0-9]+' | sed -e 's/ //g'` if [[ ! -z "$findpid" ]]; then sleep 1 kill -SIGKILL $pid fi local findpid=`ps -o pid -p $pid | egrep '[0-9]+' | sed -e 's/ //g'` if [[ ! -z "$findpid" ]]; then error "Unable Process ID $pid. Do you have enough permissions to kill it?" echo $pid > $pidfile fi echo "WARNING: you may still have some 'dangling' child processes" echo " look for processes named $GUMP_PYTHON" } # Run pygump unit tests function test { lock if [[ ! -z "$GUMP_CYGWIN" ]]; then $GUMP_PYTHON `cygpath -w $GUMP_HOME/bin/pylid-0.3/pylid.py` -c -v \ -b `cygpath -w $GUMP_HOME/pygump` -b `cygpath -w $GUMP_HOME/pygump/python` \ -i `cygpath -w $GUMP_HOME/pygump` -i `cygpath -w $GUMP_HOME/pygump/python` \ `cygpath -w $GUMP_HOME/pygump/python/gump/test` $@ else $GUMP_PYTHON $GUMP_HOME/bin/pylid-0.3/pylid.py -c -v \ -b $GUMP_HOME/pygump/python \ -i $GUMP_HOME/pygump/python \ -e $GUMP_HOME/pygump/python/gump/test \ $GUMP_HOME/pygump/python/gump/test $@ fi unlock } # Run dynagump function dynagump { # "run" is the default command local command="$1" shift if [[ -z "$command" ]]; then command="run" fi local current=`pwd` cd "$GUMP_HOME/dynagump/tools/mysqlfinder" local mysql_available=`./findmysql` if [[ ! -z "$mysql_available" ]]; then error "Cannot find mysql jdbc driver. Please retrieve it from http://www.mysql.com/products/connector/j/ and install the jarfile(s) it provides into $GUMP_HOME/dynagump/webapp/WEB-INF/lib/ If it is already installed somehwere else on your system, modify your CLASSPATH variable to point to it. You can customize the CLASSPATH variable inside a file named $GUMP_ENV_FILE if you wish." fi cd "$GUMP_HOME/dynagump" ./dynagump.sh "$command" $@ cd "$current" } # Run webgump function webgump { # "debug" is the default command local command="$1" shift if [[ -z "$command" ]]; then command="debug" fi local current=`pwd` cd "$GUMP_HOME/webgump" ./bin/$command $@ cd "$current" } # Create the gump database function create_database { local db="$1" shift if [[ -z "$db" ]]; then db="gump" fi # this will also ensure we don't delete an existing database since the # create command will fail echo command is mysqladmin create "$db" $@ '2>&1' local mysqladminresult=`mysqladmin create "$db" $@ 2>&1` if [[ ! -z "$mysqladminresult" ]]; then error "Gump failed to create the database $db. mysqladmin said: $mysqladminresult" fi local mysqlresult=`mysql $@ "$db" < $GUMP_HOME/gumpdb/src/sql/gump3-database-definition.sql` if [[ ! -z "$mysqlresult" ]]; then error "Gump failed to populate the database $db. mysql said: $mysqlresult" fi } # Update the gump-related svn checkouts on a particular server function update_host { local host="$1" shift if [[ -z "$host" ]]; then error "Please specify the host to update." fi local hostscript="$GUMP_HOME/hosts/$host-update.sh" if [[ ! -f "$hostscript" ]]; then error "Unknown host $host. Please create a file $hostscript to handle the update." fi # execute the script local $current=`pwd` cd $GUMP_HOME/hosts . $hostscript cd $current } # Create a new unit test from a template function new_unit_test { # defaults local year=`date +"%Y"` local testmodule="" local testname="" # parse CLI args while getopts ":n:m:" opt; do case $opt in n) testname="$OPTARG" ;; m) testmodule="$OPTARG" ;; \?) usage "new-unit-test" exit 1 ;; esac done shift $(($OPTIND - 1)) if [[ -z "$testmodule" ]]; then echo "ERROR: Please specify the module to test!" usage "new-unit-test" exit 1 fi if [[ -z "$testname" ]]; then testname="$testmodule" testname="${testname#gump.}" for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do upper=`echo "$i" | tr [:lower:] [:upper:]` testname="${testname//.$i/$upper}" done fi local testclassname=${testname} local testclassnamechar1=${testname:0:1} testclassnamechar1=`echo "$testclassnamechar1" | tr [:lower:] [:upper:]` local testclassnamerest=${testname:1} testclassname="${testclassnamechar1}${testclassnamerest}" local testfile="$GUMP_HOME/pygump/python/gump/test/test$testclassname.py" if [[ -f "$testfile" ]]; then error "ERROR: Test $testfile already exists!" fi cat > $testfile <