compileAndGoAbstract
compileAndGois a shebang header command that allows compiled-language source code such as Java and C++ to be used as an executable script.compileAndGocaches the compiled, executable file(s) out of sight and does make-like automatic recompilation. Wrapping source code in this way combines the convenience of a script-like, cross-platform executable source file with the benefits of a compiled language.
Here are some Hello World examples.
Writing a
|
|
Java: |
|
|
|
Mozilla Rhino JavaScript (compiled as Java .class files): |
|
|
|
C: |
|
|
Running a scriptRun the above scripts just like any other program: |
|
|
|
|
Run an ordinary source-code file like this: |
|
|
|
|
Feed some code into the standard input of |
|
|
|
|
The Java compilers also require the main class name after the compiler argument to |
|
|
|
|
Or, with the execute bit set, and with a little help from your shell, you will someday be able to do this: |
|
|
There are also more-elaborate examples.
See examples for Java javagoandmodtime, which uses JNIDirect to wrap thestatsystem callJavaScript jshelloC cgoandrealpath, a simple wrapper for the system call of the same name. Also in the download are commands that wrap system calls to manipulate extended attributes on files:listxattr,getxattr,setxattr,removexattr, adapted from Apple sample code.
You can copy and paste the compileAndGo code from this page, or you can download a compressed file that contains these web pages, the compileAndGo and cg scripts, many demo files, and a beta of JNIDirect.jar.
cacheDir |
Full path of the directory where the source and executables are cached |
commandDir |
Path to the directory containing the compileAndGo script. |
Either language or compiler is required.
All other parameters default to values appropriate for the compiler.
Compilers known to compileAndGo and cg are
javac,
gcj,
jsc,
cc, g++,
gcc,
c89,
c99,
clang,
clang++.
(See note below on JavaScript.)
language=java |
Recommended: The language, so an IDE can help you edit the file. If specified, then implies an appropriate default compiler. Known langauges are java, c, c++, and javascript. |
compiler=/usr/bin/javac |
Required (if language not supplied): The compiler. Full path not required. The compiler runs in the current directory, not in $cacheDir. |
commandName=javago |
The name of your script. Exported to the environment. Defaults to the name by which the script is invoked (which can be expressed as ${1##*/}. |
mainClass=$commandBasename |
For Java, the name of the main class. Defaults as shown. |
sourceFilename=$mainClass.java |
The source code will be extracted from the compileAndGo script and put into the file $cacheDir/$sourceFilename. Defaults as appropriate for the compiler. |
classpath=$commandDir/JNIDirect.jar |
For Java, include this in the classpath given to the compiler and the java program runner. Optional. |
executableFilename=$mainClass.class |
Compiling the source file will produce at least the file $cacheDir/$sourceFilename. Defaults as shown. |
compilerArgs=d $cacheDir -sourcepath . $cacheDir/$sourceFilename |
Arguments to the compiler. Required for jikes if CLASSPATH environment variable is not set; otherwise optional. |
linkerArgs= |
Arguments to the compiler after $compilerArgs.
Applies only to C/C++ compilers. |
execute=/usr/bin/java -cp $cacheDir:$commandDir/JNIDirect.jar $mainClass |
How to execute the program once it’s built. Defaults as appropriate for the compiler. When the compiler is javac, the path to the java program defaults to the same as the path given for javac (if any). |
verbose=1 |
1 echoes "Compiling", 2 instead echoes the commands to compile and execute, 3 also shows variable values. Higher settings show internal debugging details. Defaults to 0. |
!# |
A line containing only !# marks the end of the parameter settings.
From the next line on, it’s source code. |
compileAndGo |
A line starting with compileAndGo works to mark the end of the parameter settings,
but this usage is deprecated and replaced by '!#'. |
Tabs and spaces at beginning of line and around = signs are optional. Blank lines and comments beginning with # are ignored.
You can set and use whatever intermediate variables you may need, and you can export them.
Even though the compileAndGo program is implemented as a /bin/bash script, additional shell code in the parameter header beyond the setting of variables and echo will not work.
Elements of the cacheDir path include the modify time of the script, the compiler, and the OS and CPU architecture (as output by uname). Thus we isolate multiple versions of the source on multiple architectures and allow testing with multiple compilers without conflicts and spurious recompiles. When the script is run, if a cached executable exists in the appropriate cacheDir, we just run it; otherwise, we first make the cacheDir and compile the source there.If compiler is set to jsc
If compiler is set to javac, compileAndGo tells javac to parse for the highest language version it accepts (for example, javac -source 1.5). To determine this version, compileAndGo runs java -version.
If compiler is set to jsc, you have to put jsc.jar in your Java’s extensions folder (jre/ext/ or on Mac /Library/Java/Extensions), and compileAndGo doesn’t run a jsc command; rather, it invokes
java -cp $cacheDir${classPath:+:$classPath} org.mozilla.javascript.tools.jsc.Main $compilerArgs
#!/bin/bash
# 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
# 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.
|
compileAndGo computes a hash (using cksum) of the source code
from which it constructs a directory name (key),
which it looks for in a directory (hashtable) of cached executables.
compileAndGo could have been and should have been written decades ago. I first thought of it in March, 2004, as a special-purpose technique for Java programs.
A shell enhancement could allow you to invoke a source file as a command, similar to the way cg does.
A shell enhancement could allow a shell script to include functions written in compiled languages,
using the same hash-of-source-code trick used by compileAndGo.
A special-case Java implementation could use a single JVM to do compiling and to run the compiled program. Might make a nice addition to the JDK.
If you would like to implement compileAndGo in a compiled language, please coordinate with me.
To keep the cache clean, there is code at the end of the script you can turn on. After the invoked command has completed, it runs a background process that every once in a while looks around for old versions and unused versions and deletes them from the cache.
So far, compileAndGo has been tested only on Mac OS X 10.4.2 and two versions of linux.
You will note that the compileAndGo scripts don’t just invoke
#!/usr/local/bin/compileAndGo
but instead they invoke compileAndGo via env, like this:
#!/usr/bin/env /usr/local/bin/compileAndGo
This is because compileAndGo is itself implemented as a #! script. For more information, see the Wikipedia article on Shebang
ls
classpath and commandDir and exports commandName. Also, download link added
CLASSPATH setting in the parameters, echo, and bug fixes
commandName is now optional.
cgs variant of the cg command
~/.TemporaryItems/ on Mac; optional automatic cache cleaning
javac to parse for the highest source version it can accept
language parameter added