626 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			626 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| # See notice of copyright and license at end.
 | |
| 
 | |
| # If you use this program for a #! script, please include the following
 | |
| # as the second line of the script:
 | |
| # See http://Yost.com/computers/compileAndGo
 | |
| 
 | |
| # Bug: doesn't recompile if invoked file is a symlink. Fixed?
 | |
| # Needs a call to realpath.
 | |
| 
 | |
| ## Bug: The following fails for the cg command after fixing it to work for compileAndGo
 | |
| # Something about evaluation order of the variables in the source header.
 | |
| # compiler = gcc
 | |
| # compilerArgs = -O2 -o $cacheDir/$executableFilename $cacheDir/$sourceFilename
 | |
| 
 | |
| if [ $# == 1 -a "$1" == --help ]; then
 | |
|     echo "compileAndGo: see http://Yost.com/computers/compileAndGo"
 | |
|     exit 0
 | |
| fi
 | |
| 
 | |
| # The #! compileAndGo script that invoked us.
 | |
| # If $0 is a symlink, this is the invoked name, not the symlink referent name
 | |
| typeset -r commandFile="$1"
 | |
| 
 | |
| pathOfDirPartOfExecPath() {
 | |
|   if [[ -h "$1" ]] ; then
 | |
|     local lsout="$(ls -l "$1")"
 | |
|     local referent="${lsout#* -> }"
 | |
|     pathOfDirPartOfExecPath "$referent"
 | |
|   elif [[ -d "${1%/*}" ]] ; then
 | |
|     echo "${1%/*}"
 | |
|   else
 | |
|     echo .
 | |
|   fi
 | |
| }
 | |
| typeset -r commandDir="$(pathOfDirPartOfExecPath "$0")/"
 | |
| # If $0 is a symlink, this is the invoked name, not the symlink referent name
 | |
| 
 | |
| ourName=${0##*/}
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| 
 | |
| extensionToLanguage() {
 | |
|   case "$1" in
 | |
|   js)
 | |
|     echo javascript
 | |
|     ;;
 | |
|   java)
 | |
|     echo java
 | |
|     ;;
 | |
|   c)
 | |
|     echo c
 | |
|     ;;
 | |
|   cp | cpp | C | cxx | cc)
 | |
|     echo c++
 | |
|     ;;
 | |
|   *)
 | |
|     ;;
 | |
|   esac
 | |
| }
 | |
| 
 | |
| languageToCompiler() {
 | |
|   case "$1" in
 | |
|   javascript)
 | |
|     echo jsc
 | |
|     ;;
 | |
|   java)
 | |
|     echo javac
 | |
|     ;;
 | |
|   c)
 | |
|     if [[ -x /usr/bin/gcc ]] ; then
 | |
|       echo /usr/bin/gcc
 | |
|     else
 | |
|       echo /usr/bin/cc
 | |
|     fi
 | |
|     ;;
 | |
|   c++)
 | |
|     echo /usr/bin/g++
 | |
|     ;;
 | |
|   *)
 | |
|     ;;
 | |
|   esac
 | |
| }
 | |
| 
 | |
| echo2() {
 | |
|   # This works for sh, ksh, and bash, but not zsh.
 | |
|   echo "$@"
 | |
| }
 | |
| 
 | |
| echoVariables() {
 | |
|   echo 1>&2 $1
 | |
|   if [[ ! $ourName == cg ]] ; then
 | |
|     # Echo all but the builtins.
 | |
|     echo 1>&2 "$(
 | |
|       eval "$(
 | |
|         echo "$cmdSetters" \
 | |
|         | grep -v 'commandBasename
 | |
| commandDir
 | |
| sourceFilename
 | |
| language
 | |
| mainClass
 | |
| executableFilename
 | |
| compilerDir
 | |
| classPath
 | |
| compilerArgs
 | |
| linkerArgs
 | |
| execute
 | |
| firstArgIsCommandName
 | |
| verbose' \
 | |
|         | sed "
 | |
|             s,^eval,echo,
 | |
|             s,=, = ,
 | |
|             s,',,g
 | |
|           "
 | |
|       )" \
 | |
|       | sed "
 | |
|           s,= ,= ',
 | |
|           s,$,',
 | |
|         "
 | |
|     )"
 | |
|   else
 | |
|     echo 1>&2 commandName         = "$commandName"
 | |
|     echo 1>&2 compiler            = "$compiler"
 | |
|   fi
 | |
|   # Echo the builtins.
 | |
|   echo 1>&2 commandBasename       = "$commandBasename"
 | |
|   echo 1>&2 commandDir            = "$commandDir"
 | |
|   echo 1>&2 sourceFilename        = "$sourceFilename"
 | |
|   echo 1>&2 language              = "$language"
 | |
|   echo 1>&2 mainClass             = "$mainClass"
 | |
|   echo 1>&2 executableFilename    = "$executableFilename"
 | |
|   echo 1>&2 compilerDir           = "$compilerDir"
 | |
|   echo 1>&2 classPath             = "$classPath"
 | |
|   echo 1>&2 compilerArgs          = "$compilerArgs"
 | |
|   echo 1>&2 linkerArgs            = "$linkerArgs"
 | |
|   echo 1>&2 execute               = "$execute"
 | |
|   echo 1>&2 firstArgIsCommandName = "$firstArgIsCommandName"
 | |
|   echo 1>&2 verbose               = "$verbose"
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| 
 | |
| # If we use zsh, we could do this:
 | |
| # zmodload zsh/stat
 | |
| # stat -F "%Y-%m-%d_%H-%M-%S" +mtime .
 | |
| 
 | |
| ls-linux() {
 | |
|   # 11742 2005-07-28 11:54:01.000000000
 | |
|   (
 | |
|     ls -dl --full-time --time-style=full-iso "$1" \
 | |
|     | sed 's,.*[0-9] \([12][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\) \([0-9][0-9]\):\([0-9][0-9]\):\([0-9][0-9]\).[0-9]* .*,\1.\2-\3-\4,'
 | |
|   ) 2> /dev/null
 | |
| }
 | |
| 
 | |
| ls-oldlinux() {
 | |
|   # 11742 Tue Mar 01 17:22:50 2005
 | |
|   (
 | |
|     ls -dl --full-time "$1" \
 | |
|     | sed 's,.*[0-9] ... \(...\) \([0-9][0-9]\) \([0-9][0-9]\):\([0-9][0-9]\):\([0-9][0-9]\) \([12][0-9][0-9][0-9]\) .*,\4-\1-\2.\3-\5-\6,'
 | |
|   ) 2> /dev/null
 | |
| }
 | |
| 
 | |
| ls-bsd() {
 | |
|   # 11742 Jul 28 12:38:31 2005
 | |
|   (
 | |
|     ls -dlT "$1" \
 | |
|     | awk '
 | |
|       BEGIN {
 | |
|         months["Jan"] = "01" ; months["Feb"] = "02" ; months["Mar"] = "03"
 | |
|         months["Apr"] = "04" ; months["May"] = "05" ; months["Jun"] = "06"
 | |
|         months["Jul"] = "07" ; months["Aug"] = "08" ; months["Sep"] = "09"
 | |
|         months["Oct"] = "10" ; months["Nov"] = "11" ; months["Dec"] = "12"
 | |
|       }
 | |
|       {
 | |
|         month = sprintf(months[$6]) ; day = $7 ; time = $8 ; year = $9 # What about Europe?
 | |
|         gsub(":", "-", time)
 | |
|         date = year     "-" month     "-" day     "_" time
 | |
|         print date
 | |
|       }
 | |
|     '
 | |
|   ) 2> /dev/null
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| 
 | |
| set -e
 | |
| 
 | |
| sourceInput=
 | |
| case $ourName in
 | |
| cg)
 | |
|   # Invoked as cg
 | |
|   if [[ $# == 0 ]] ; then
 | |
|     echo 1>&2 "Usage: cg sourcefile.<extension> [ args ... ]"
 | |
|     exit 2
 | |
|   fi
 | |
|   sourceInput="$1"
 | |
|   export commandName=${commandFile##*/}
 | |
|   commandBasename="${commandName%.*}"
 | |
|   commandExtension="${commandFile##*.}"
 | |
|   sourceFilename=$commandName
 | |
|   language=$(extensionToLanguage $commandExtension)
 | |
|   compiler=$(languageToCompiler $language)
 | |
|   ;;
 | |
| cgs)
 | |
|   sourceInput=/dev/stdin
 | |
|   export commandName=$ourName
 | |
|   commandBasename=$commandName
 | |
|   compilerOpt="$1"
 | |
|   case "$compilerOpt" in
 | |
|   -jsc)
 | |
|     sourceFileName=$commandName.js
 | |
|     ;;
 | |
|   -javac | -gcj | -jikes)
 | |
|     shift
 | |
|     export commandName=$1
 | |
|     commandBasename=$commandName
 | |
|     sourceFileName=$commandName.java
 | |
|     ;;
 | |
|   -gcc | -c99 | -c89 | -cc)
 | |
|     sourceFileName=$commandName.c
 | |
|     ;;
 | |
|   -g++)
 | |
|     sourceFileName=$commandName.cp
 | |
|     ;;
 | |
|   "")
 | |
|     echo 1>&2 cgs: missing compiler option
 | |
|     exit 2
 | |
|     ;;
 | |
|   *)
 | |
|     echo 1>&2 cgs: unknown compiler option: "$compilerOpt"
 | |
|     exit 2
 | |
|     ;;
 | |
|   esac
 | |
|   compiler=${compilerOpt/-/}
 | |
|   ;;
 | |
| *)
 | |
|   sourceInput=
 | |
|   # Invoked as compileAndGo
 | |
|   # Collect the variable declarations at the top of $commmandFile.
 | |
|   declarations="$(
 | |
|     sed -n '
 | |
|       s,^[   ]*,,
 | |
|       /^!#$/q
 | |
|       /^compileAndGo/q
 | |
|       /^[   ]*#/d
 | |
|       s,^commandName,export commandName,
 | |
|       /^echo[   ]/{
 | |
|         s/$/ ;/p
 | |
|         d
 | |
|       }
 | |
|       s,\$\({*commandBasename\),\\$\1,g
 | |
|       s,\$\({*commandDir\),\\$\1,g
 | |
|       s,\$\({*sourceFilename\),\\$\1,g
 | |
|       s,\$\({*language\),\\$\1,g
 | |
|       s,\$\({*mainClass\),\\$\1,g
 | |
|       s,\$\({*executableFilename\),\\$\1,g
 | |
|       s,\$\({*compilerDir\),\\$\1,g
 | |
|       s,\$\({*classPath\),\\$\1,g
 | |
|       s,\$\({*compilerArgs\),\\$\1,g
 | |
|       s,\$\({*linkerArgs\),\\$\1,g
 | |
|       s,\$\({*execute\),\\$\1,g
 | |
|       s,\$\({*cacheDir\),\\$\1,g
 | |
|       s,\$\({*firstArgIsCommandName\),\\$\1,g
 | |
|       s,[   ]*=[   ]*,=",
 | |
|       s,$," ;,
 | |
|       p
 | |
|     ' "$commandFile"
 | |
|   )"
 | |
|   eval $declarations
 | |
|   [[ ! -z ${commandName:="${1##*/}"} ]]
 | |
|   commandBasename="${commandName%.*}"
 | |
|   if (( 0$verbose >= 5 )) ; then
 | |
|     echo 1>&2 \=== Declarations
 | |
|     echo 1>&2 "$declarations"
 | |
|   fi
 | |
|   if [[ -z "$compiler" ]] ; then
 | |
|     if [[ -z "$language" ]] ; then
 | |
|       echo 1>&2 compileAndGo: compiler or language must be set
 | |
|       trouble=true
 | |
|     fi
 | |
|     compiler=$(languageToCompiler $language)
 | |
|   fi
 | |
|   if [[ ! -z "$trouble" ]] ; then
 | |
|     exit 2
 | |
|   fi
 | |
|   ;;
 | |
| esac
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Collect the source code
 | |
| 
 | |
| newsed() {
 | |
|   local arg
 | |
|   if sed --regex-extended < /dev/null >& /dev/null ; then
 | |
|     arg=--regex-extended
 | |
|   else
 | |
|     arg=-E
 | |
|   fi
 | |
|   sed $arg "$@"
 | |
| }
 | |
| 
 | |
| case "$sourceInput" in
 | |
| /dev/stdin)
 | |
|   # from stdin
 | |
|   sourceCode=$(cat)
 | |
|   ;;
 | |
| '')
 | |
|   # from the end of $commandFile
 | |
|   sourceCode=$(
 | |
|     newsed '
 | |
|       1,/^(!#|[   ]*compileAndGo)/s,.*,,
 | |
|     ' "$commandFile"
 | |
|   )
 | |
|   if [[ -z "$sourceCode" ]] ; then
 | |
|     echo 1>&2 "$commandName: Missing '#!compileAndGo' line before source code starts."
 | |
|     exit 2
 | |
|   fi
 | |
|   ;;
 | |
| *)
 | |
|   # from the filename as first argument
 | |
|   sourceCode=$(cat $1)
 | |
|   ;;
 | |
| esac
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Construct the cacheDir variable.
 | |
| 
 | |
| abi=$(uname -sm)
 | |
| abi=${abi// /-}
 | |
| 
 | |
| # Why must I use `` instead of $() here?
 | |
| id=`
 | |
|   case "$sourceInput" in
 | |
|   /dev/stdin)
 | |
|     local tmp=($(echo "$sourceCode" | cksum))
 | |
|     echo ${tmp[0]}${tmp[1]}
 | |
|     ;;
 | |
|   *)
 | |
|     case "$abi" in
 | |
|     Linux* | CYGWIN*)
 | |
|       ls-linux "$1" \
 | |
|       || ls-oldlinux "$1"
 | |
|       ;;
 | |
|     *)
 | |
|       ls-bsd "$1" \
 | |
|       || ls-linux "$1" \
 | |
|       || ls-oldlinux "$1"
 | |
|       ;;
 | |
|     esac \
 | |
|     || (
 | |
|       local tmp=($(echo "$sourceCode" | cksum))
 | |
|       echo ${tmp[0]}${tmp[1]}
 | |
|     )
 | |
|     ;;
 | |
|   esac
 | |
| `
 | |
| compilerPath=$(type -p "$compiler" 2> /dev/null) || compilerPath=$compiler
 | |
| realHOME=$(eval 'echo ~'$(whoami))
 | |
| 
 | |
| if [[ -x $realHOME/Library/Caches ]] ; then
 | |
|   # Mac OS X
 | |
|   cacheDirRoot=$realHOME/Library/Caches/CompileAndGo
 | |
| else
 | |
|   cacheDirRoot=$realHOME/.compileAndGo
 | |
| fi
 | |
| cacheDirParent=$cacheDirRoot/${commandName}
 | |
| cacheDir=$cacheDirParent/$abi/${id}_${compilerPath//\//-}
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Apply defaults and then set the variables again.
 | |
| 
 | |
| compilerName=${compiler##*/}
 | |
| 
 | |
| # Some settings common among different compiler groups:
 | |
| case $compilerName in
 | |
| javac* | jikes*)
 | |
|   [[ ! -z ${mainClass:="$commandBasename"} ]]
 | |
|   [[ ! -z ${sourceFilename:="${mainClass}.java"} ]]
 | |
|   ;;
 | |
| gcj*)
 | |
|   [[ ! -z ${mainClass:="$commandBasename"} ]]
 | |
|   [[ ! -z ${sourceFilename:="${mainClass}.java"} ]]
 | |
|   [[ ! -z ${executableFilename:="$commandBasename"} ]]
 | |
|   [[ ! -z ${execute:="PATH=$cacheDir:$PATH $executableFilename"} ]]
 | |
|   ;;
 | |
| gcc* | g++* | c89* | c99*)
 | |
|   [[ ! -z ${executableFilename:="$commandBasename"} ]]
 | |
|   [[ ! -z ${execute:="PATH=$cacheDir:$PATH $executableFilename"} ]]
 | |
|   ;;
 | |
| esac
 | |
| 
 | |
| case $compilerName in
 | |
| jsc*)
 | |
|   [[ ! -z ${mainClass:="$commandBasename"} ]]
 | |
|   [[ ! -z ${sourceFilename:="${mainClass}.js"} ]]
 | |
|   [[ ! -z ${executableFilename:="${mainClass}.class"} ]]
 | |
|   [[ ! -z "${execute:="${javaBinDir}java -cp $cacheDir${classPath:+:$classPath} $mainClass"}" ]]
 | |
|   [[ ! -z "${compilerArgs:="-o $executableFilename $cacheDir/$sourceFilename"}" ]]
 | |
|   compileCmd="java -cp $cacheDir${classPath:+:$classPath} org.mozilla.javascript.tools.jsc.Main $compilerArgs"
 | |
|   ;;
 | |
| javac* | jikes*)
 | |
|   [[ ! -z ${executableFilename:="${mainClass}.class"} ]]
 | |
|   sourceVersion=
 | |
|   case $compilerName in
 | |
|   javac*)
 | |
|     if [[ $compilerName == $compiler ]] ; then
 | |
|       compilerDir=
 | |
|     else
 | |
|       compilerDir=${compiler%/*}/
 | |
|     fi
 | |
|     [[ ! -z "${execute:="${compilerDir}java -cp $cacheDir${classPath:+:$classPath} $mainClass"}" ]]
 | |
|     # Prepare to tell javac to compile for the latest language version it supports
 | |
|     sourceVersion="-source $(${compilerDir}java -version 2>&1 | sed -n '1{s/.*"\(.*\)".*/\1/; s/^1\.//; s/\..*//; p}')"
 | |
|     ;;
 | |
|   jikes*)
 | |
|     if [[ -z "$classPath" && -z "$compilerArgs" && -z "$CLASSPATH" ]] ; then
 | |
|       # Mac: export CLASSPATH=/System/Library/Frameworks/JavaVM.framework/Classes/classes.jar
 | |
|       echo 1>&2 compileAndGo: for jikes, if neither classPath nor CLASSPATH are set, compilerArgs must be set.
 | |
|       exit 2
 | |
|     fi
 | |
|     [[ ! -z "${execute:="java -cp $cacheDir${classPath:+:$classPath} $mainClass"}" ]]
 | |
|     ;;
 | |
|   esac
 | |
|   [[ ! -z "${compilerArgs:="$sourceVersion -d $cacheDir -sourcepath . ${classPath:+-classpath $classPath} $cacheDir/$sourceFilename"}" ]]
 | |
|   compileCmd="$compiler $compilerArgs"
 | |
|   ;;
 | |
| gcj*)
 | |
|   [[ ! -z ${compilerArgs:="--main=$mainClass -o $cacheDir/$commandBasename $cacheDir/$sourceFilename"} ]]
 | |
|   compileCmd="$compiler $compilerArgs $linkerArgs"
 | |
|   ;;
 | |
| gcc* | g++* | c89* | c99* | clang | clang++)
 | |
|   case $compilerName in
 | |
|   cc* | gcc* | c89* | c99* | clang)
 | |
|     [[ ! -z ${sourceFilename:="${commandName}.c"} ]]
 | |
|     ;;
 | |
|   g++* | clang++)
 | |
|     [[ ! -z ${sourceFilename:="${commandName}.cp"} ]]
 | |
|     ;;
 | |
|   esac
 | |
|   [[ ! -z ${compilerArgs:="-O2 -o $cacheDir/$executableFilename $cacheDir/$sourceFilename"} ]]
 | |
|   compileCmd="$compiler $compilerArgs $linkerArgs"
 | |
|   ;;
 | |
| esac
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Set the variables
 | |
| 
 | |
| if [[ ! $ourName == cg ]] ; then
 | |
|   vars=$(
 | |
|     echo "$declarations" \
 | |
|     | sed -n 's,\([^=]*\)=.*,\1,p'
 | |
|   )
 | |
|   cmdSetters=$(
 | |
|     for x in $vars
 | |
|     do
 | |
|       echo eval "'"${x}='"'$(eval echo \$$x)'"'"'"
 | |
|     done
 | |
|   )
 | |
|   eval "$cmdSetters"
 | |
| fi
 | |
| if (( 0$verbose >= 3 )) ; then
 | |
|   echoVariables "=== the variables before defaults"
 | |
| fi
 | |
| if [[ $ourName == cg ]] ; then
 | |
|   if (( 0$verbose >= 4 )) ; then
 | |
|     echo 1>&2 \=== eval command to set variables
 | |
|     echo2 1>&2 eval "$cmdSetters"
 | |
|   fi
 | |
| fi
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Check that all the required variables are set.
 | |
| for x in sourceFilename executableFilename compilerArgs execute
 | |
| do
 | |
|   eval 'if [ -z "'\$$x'" ] ; then trouble=true ; fi'
 | |
| done
 | |
| if [[ ! -z "$trouble" ]] ; then echo 1>&2 compileAndGo: unknown compiler setting "$compiler" ; exit 2 ; fi
 | |
| for x in sourceFilename executableFilename compilerArgs execute
 | |
| do
 | |
|   eval 'if [ -z "'\$$x'" ] ; then echo 1>&2 compileAndGo: $x must be set ; fi'
 | |
| done
 | |
| if [[ ! -z "$trouble" ]] ; then exit 2 ; fi
 | |
| 
 | |
| [[ ! -z ${firstArgIsCommandName:=false} ]]
 | |
| [[ ! -z ${verbose:=0} ]]
 | |
| 
 | |
| eval "$cmdSetters"
 | |
| 
 | |
| if (( 0$verbose >= 3 )) ; then
 | |
|   echoVariables "=== the variables after defaults"
 | |
| fi
 | |
| 
 | |
| #set -x
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Compile if necessary
 | |
| 
 | |
| # shorthand
 | |
| cachedExecutable=$cacheDir/$executableFilename
 | |
| 
 | |
| # The security precautions go like this:
 | |
| # The executable and the folder in which it resides are
 | |
| # * owned by user
 | |
| # * 700 permissions (rwx------)
 | |
| # The important facts are:
 | |
| # * Only the user or root can chmod a file or folder owned by him.
 | |
| # * Only the user or root can write into a file or folder that is 700.
 | |
| # * Only root can chown a file or folder to the user.
 | |
| # so only the user or root can construct a suitable file in the suitable
 | |
| # folder.  No one else can.  That's about as good as it can get on unix.
 | |
| # The attack would be limited to finding some existing folder containing
 | |
| # an executable of the correct name, both owned by the user and 700,
 | |
| # then moving the folder into the appropriate path.
 | |
| # The implementation should be expanded to require that all folders from
 | |
| # $cacheDir through $cacheDirParent must be owned by user and be 700.
 | |
| if [[ ! -O $cachedExecutable ]] ; then
 | |
|   if [[ -e $cachedExecutable ]] ; then
 | |
|     echo 1>&2 "$commandName: Aborting because $cachedExecutable exists,"
 | |
|     echo 1>&2 "$commandName: and you don't own it."
 | |
|     echo 1>&2 "$commandName: This is a possible security violation."
 | |
|     exit 2
 | |
|   fi
 | |
| 
 | |
|   # Try to make it harder for others to tamper with our cache.
 | |
|   umask 077
 | |
|   # Insist that $cacheDirParent is a directory and is owned by the user.
 | |
|   if [[     -d $cacheDirParent ]] ; then
 | |
|     if [[ ! -O $cacheDirParent ]] ; then
 | |
|       echo 1>&2 "$commandName: Aborting because $cacheDirParent/ exists, and you don't own it."
 | |
|       echo 1>&2 "$commandName: This is a security risk."
 | |
|       exit 2
 | |
|     fi
 | |
|     chmod 700 $cacheDirParent
 | |
|   else
 | |
|     mkdir -p $cacheDirParent
 | |
|     echo > $cacheDirParent/../README "See http://Yost.com/computers/compileAndGo"
 | |
|   fi
 | |
| 
 | |
|   mkdir -p $cacheDir
 | |
| 
 | |
|   # Compile the source.
 | |
|   if (( 0$verbose == 1 )) ; then
 | |
|     echo 1>&2 "[ $commandName: compiling. ]"
 | |
|   elif (( 0$verbose >= 2 )) ; then
 | |
|     echo  -n 1>&2 "[ "
 | |
|     echo2 -n 1>&2    "$compileCmd"
 | |
|     echo     1>&2                " ]"
 | |
|   fi
 | |
|   echo "$sourceCode" > $cacheDir/$sourceFilename
 | |
|   eval $compileCmd
 | |
| 
 | |
|   # Make a canonical name we can look at to determine access time.
 | |
|   ln -f $cachedExecutable $cacheDir/.executable
 | |
| fi
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Execute the built program.
 | |
| 
 | |
| if [[ "$firstArgIsCommandName" != true ]] ; then
 | |
|   shift
 | |
| fi
 | |
| 
 | |
| if (( 0$verbose >= 2 )) ; then
 | |
|   echo  -n 1>&2 "[ "
 | |
|   echo2 -n 1>&2    $execute "$@"
 | |
|   echo     1>&2                " ]"
 | |
| fi
 | |
| eval "$execute"' "$@"'
 | |
| status=$?
 | |
| 
 | |
| if [[ true ]] ; then
 | |
|   # Run a background task to clean the cache occasionally.
 | |
|   (
 | |
|     # Check every this-many days.
 | |
|     checkInterval="-mtime -7"
 | |
|     # Max number of days a version cam be unused and not be removed.
 | |
|     maxAge="-atime +14"
 | |
|     # Every $checkInterval days, remove stuff not used in $maxAge days.
 | |
|     stamp=$(nice -n 20 find $cacheDirRoot -maxdepth 1 -name .timestamp $checkInterval)
 | |
|     if [[ ! -z "$stamp" ]] ; then
 | |
|       # Too soon
 | |
|       exit
 | |
|     fi
 | |
|     nice -n 20 touch $cacheDirRoot/.timestamp
 | |
|     # Remove dirs of executable versions not accessed in the last $maxAge days.
 | |
|     candidates=$(nice -n 20 find $cacheDirRoot -mindepth 3 -name .executable $maxAge)
 | |
|     if [[ ! -z "$candidates" ]] ; then
 | |
|       #echo "$candidates"
 | |
|       echo "$candidates" \
 | |
|       | nice -n 20 sed 's,/.executable,,' \
 | |
|       | nice -n 20 xargs rm -rf
 | |
|     fi
 | |
|   ) \
 | |
|   > /dev/null 2>&1 \
 | |
|   &
 | |
| fi
 | |
| 
 | |
| exit $status
 | |
| 
 | |
| # Copyright 2005-2010 Dave Yost <Dave@Yost.com>
 | |
| # All rights reserved.
 | |
| # This version is
 | |
| #   compileAndGo 5.0 2010-11-06
 | |
| # which at time of this publication can be found at:
 | |
| #   http://Yost.com/computers/compileAndGo
 | |
| # Redistribution and use in the form of source code or derivative data built
 | |
| # from the source code, with or without modification, are permitted provided
 | |
| # that the following conditions are met:
 | |
| # 1. THE USER AGREES THAT THERE IS NO WARRANTY.
 | |
| # 2. If and only if appropriate, the above phrase "This version is" must be
 | |
| #    followed by the phrase "a modified form of" or "extracted from" or
 | |
| #    "extracted and modified from".
 | |
| # 3. Redistributions of source code must retain this notice intact.
 | |
| # 4. Redistributions in the form of derivative data built from the source
 | |
| #    code must reproduce this notice intact in the documentation and/or other
 | |
| #    materials provided with the distribution, and each file in the derivative
 | |
| #    data must reproduce any Yost.com URI included in the original distribution.
 | |
| # 5. Neither the name of Dave Yost nor the names of its contributors may be
 | |
| #    used to endorse or promote products derived from this software without
 | |
| #    specific prior written permission.
 | |
| # 6. Written permission by the author is required for redistribution as part
 | |
| #    of a commercial product.
 | |
| # This notice comprises all text from "Copyright" above through the end of
 | |
| # this sentence.
 |