2013-08-27 15:14:44 +04:00
|
|
|
#!/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.
|
|
|
|
|
2015-08-20 07:58:17 +04:00
|
|
|
# If you use this program for a #! script, please include the following
|
2013-08-27 15:14:44 +04:00
|
|
|
# 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" \
|
2015-08-20 07:58:17 +04:00
|
|
|
| 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,'
|
2013-08-27 15:14:44 +04:00
|
|
|
) 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.
|
2015-08-20 07:58:17 +04:00
|
|
|
# so only the user or root can construct a suitable file in the suitable
|
2013-08-27 15:14:44 +04:00
|
|
|
# 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
|
2015-08-20 07:58:17 +04:00
|
|
|
# an executable of the correct name, both owned by the user and 700,
|
2013-08-27 15:14:44 +04:00
|
|
|
# then moving the folder into the appropriate path.
|
2015-08-20 07:58:17 +04:00
|
|
|
# The implementation should be expanded to require that all folders from
|
2013-08-27 15:14:44 +04:00
|
|
|
# $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
|
2015-08-20 07:58:17 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
mkdir -p $cacheDir
|
2015-08-20 07:58:17 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
# 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
|
2015-08-20 07:58:17 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
# 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
|
2015-08-20 07:58:17 +04:00
|
|
|
# Redistribution and use in the form of source code or derivative data built
|
|
|
|
# from the source code, with or without modification, are permitted provided
|
2013-08-27 15:14:44 +04:00
|
|
|
# that the following conditions are met:
|
|
|
|
# 1. THE USER AGREES THAT THERE IS NO WARRANTY.
|
2015-08-20 07:58:17 +04:00
|
|
|
# 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
|
2013-08-27 15:14:44 +04:00
|
|
|
# "extracted and modified from".
|
|
|
|
# 3. Redistributions of source code must retain this notice intact.
|
2015-08-20 07:58:17 +04:00
|
|
|
# 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
|
2013-08-27 15:14:44 +04:00
|
|
|
# data must reproduce any Yost.com URI included in the original distribution.
|
2015-08-20 07:58:17 +04:00
|
|
|
# 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
|
2013-08-27 15:14:44 +04:00
|
|
|
# specific prior written permission.
|
2015-08-20 07:58:17 +04:00
|
|
|
# 6. Written permission by the author is required for redistribution as part
|
2013-08-27 15:14:44 +04:00
|
|
|
# of a commercial product.
|
2015-08-20 07:58:17 +04:00
|
|
|
# This notice comprises all text from "Copyright" above through the end of
|
2013-08-27 15:14:44 +04:00
|
|
|
# this sentence.
|