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 '1s,[^"]*"\([1-9][1-9]*\.[1-9][1-9]*\).*,\1,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.
 |