#!/bin/bash # # This is a "compiler" for Amber code. Run without arguments for help. # # Get Amber root directory from the location of this script so that # we can find the st and js directories etc. # Earlier we used this but it does not work on Mac # Amber=$(readlink -f `dirname ${0}`/..) TARGET=`dirname ${0}`/.. pushd . >/dev/null cd $TARGET AMBER="`\pwd -P`" popd >/dev/null function usage { cat <.js file is linked together based on the options: -l library1,library2 Additionally add listed libraries (no spaces or .js) in listed order. -i file Add library initializer instead of default $AMBER/js/init.js -m class Add at end a call to #main in class . -M file Add at end javascript file acting as main. -o Optimize each js file using the Google closure compiler. Using Closure compiler found at ~/compiler.jar -O Optimize final .js using the Google closure compiler. Using Closure compiler found at ~/compiler.jar -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS -d Additionally export code for deploy - stripped from source etc. Uses suffix ".deploy.js" in addition to any explicit given suffic using -s. -s suffix Add to compiled js files so that File.st is compiled into File..js. -S suffix Use for all libraries accessed using -L or -l. This makes it possible to have multiple flavors of Amber and libraries in the same place. Example invocations: Just compile Kernel-Objects.st to Kernel-Objects.js: amberc Kernel-Objects.st Compile Hello.st to Hello.js and create complete program called Program.js and adding a call to class method #main in class Hello: amberc -m Hello Hello.st Program Compile two .st files into corresponding .js files, and link with specific myboot.js, myKernel.js, myinit.js and main.js and create complete program called Program.js: amberc -M main.js myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program ENDOFHELP exit 1; } # Check we at least got one argument if [ -z $1 ] ; then usage fi # Define our predefined library combinations KERNEL="boot Kernel-Objects Kernel-Classes Kernel-Methods Kernel-Collections Kernel-Exceptions Kernel-Transcript" COMPILER="$KERNEL parser Compiler" # Predefined initializer INITIALIZER="$AMBER/js/init.js" # Default values ENV= INIT=$INITIALIZER MAIN= MAINFILE= BASE=$KERNEL LOAD= CLOSUREOPTS= # Ok, bad coding practice but hey, who would use such a suffix? SUFFIX=no-silly-suffix SUFFIXUSED= DEPLOY=false NODECOMPILE=nodecompile # Read options and shift them away while getopts "l:i:m:M:oOAds:S:h?" o; do case "$o" in l) LOAD=$OPTARG;; i) INIT=$OPTARG;; m) MAIN=$OPTARG;; M) MAINFILE=$OPTARG;; o) CLOSURE=true CLOSUREPARTS=true;; O) CLOSURE=true CLOSUREFULL=true;; A) CLOSURE=true CLOSUREOPTS="$CLOSUREOPTS --compilation_level ADVANCED_OPTIMIZATIONS" CLOSUREFULL=true;; d) DEPLOY=true;; s) SUFFIX=$OPTARG SUFFIXUSED=$SUFFIX;; S) LOADSUFFIX=$OPTARG SUFFIXUSED=$SUFFIX;; h) usage;; [?]) usage;; esac done shift $(($OPTIND - 1)) # Check for Closure compiler and Java if [ ! -z $CLOSURE ]; then java > /dev/null if [ $? -eq 0 ]; then if [ ! -f ~/compiler.jar ]; then echo "Can not find Closure compiler at ~/compiler.jar" exit 1 fi else echo "java is not installed and is needed for -O, -A or -o (Closure compiler)." exit 1 fi fi # Function for looking up listed js files function resolvejs { FNAME="$1$LOADSUFFIX.js" if [ -f $FNAME ]; then RESOLVED="$FNAME" else if [ -f $AMBER/js/$FNAME ]; then RESOLVED="$AMBER/js/$FNAME" else echo "Javascript file not found: $FNAME" exit 1 fi fi } # Resolve listed libraries in $BASE deparated by spaces for FILE in $BASE do resolvejs $FILE TOBASE="$TOBASE $RESOLVED" done # Resolve listed libraries in $LOAD separated by , LOAD=${LOAD//,/\ } for FILE in $LOAD do resolvejs $FILE TOLOAD="$TOLOAD $RESOLVED" done # Resolve COMPILER for FILE in $COMPILER do resolvejs $FILE TOCOMPILER="$TOCOMPILER $RESOLVED" done # Add supplied libraries we have not already loaded (they are already resolved) #for FILE in $EXTRA #do # resolvejs $FILE # TOEXTRA="$TOEXTRA $RESOLVED" #done TOCOMPILER="$TOCOMPILER$TOLOAD" # Resolve init and nodecompile THEREST="init $AMBER/bin/$NODECOMPILE" for FILE in $THEREST do resolvejs $FILE TOCOMPILER="$TOCOMPILER $RESOLVED" done # Add supplied libraries LIBS="$TOBASE $TOLOAD" # Get a unique tempdir and make it get auto removed on exit TMPDIR=`mktemp -d amberc.XXXXXX` trap "rm -rf $TMPDIR" EXIT # -------------------------------------------------- # Collect libraries and Smalltalk files looking # both locally and in $AMBER/js and $AMBER/st # -------------------------------------------------- PROGRAM= until [ "$*" = "" ] do case $1 in *.st) CATEGORY=`basename $1 .st` if [ -f "$1" ]; then COMPILE="$COMPILE $1 $CATEGORY" COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js" else if [ -f $AMBER/st/$1 ]; then COMPILE="$COMPILE $AMBER/st/$1 $CATEGORY" COMPILED="$COMPILED $CATEGORY$SUFFIXUSED.js" else echo "Amber file not found: $1" exit 1 fi fi ;; *.js) resolvejs $1 LIBS="$LIBS $RESOLVED" ;; *) # Will end up being the last non js/st argument PROGRAM=$1 ;; esac shift done # -------------------------------------------------- # Actual compilation phase of collected .st files # -------------------------------------------------- # Create compiler dynamically cat $TOCOMPILER > $TMPDIR/compiler.js # Compile all collected .st files to .js echo "Loading libraries$TOCOMPILER and compiling ..." node $TMPDIR/compiler.js $DEPLOY $SUFFIX $COMPILE # Verify all .js files corresponding to .st files were created, otherwise exit IFS=" " for FILE in $COMPILED do if [ ! -f "$FILE" ]; then echo "Failed compilation of $FILE, exiting." exit 1 fi done if [ ! -z $CLOSUREPARTS ]; then echo "Compiling all js files using Google closure compiler." ALLJSFILES="$COMPILED $LIBS" for FILE in $ALLJSFILES do mv $FILE $FILE.original java -jar ~/compiler.jar $CLOSUREOPTS --js $FILE.original --js_output_file $FILE rm $FILE.original done fi if [ -z $PROGRAM ]; then echo "Done." exit 0 fi # -------------------------------------------------- # Now we start composing resulting javascript file. # -------------------------------------------------- # Add collected libraries to libs.js file. if [ ! -z "$LIBS" ]; then echo "Adding libraries $LIBS ..." cat $LIBS > $TMPDIR/libs.js LIBS=$TMPDIR/libs.js fi echo "Adding Amber code$COMPILED ..." # Check for init file if [ ! -z "$INIT" ]; then if [ -f "$INIT" ]; then echo "Adding initializer $INIT ..." else echo "Can not find init file $INIT, exiting." exit 1 fi fi # Check for adding main if [ ! -z "$MAIN" ]; then echo "Adding call to $MAIN class >> main ..." echo "smalltalk.$MAIN._main()" > $TMPDIR/main.js MAIN=$TMPDIR/main.js fi # Check for adding main file if [ ! -z "$MAINFILE" ]; then if [ -f "$MAINFILE" ]; then echo "Adding main as $MAINFILE ..." else echo "Can not find main file $MAINFILE, exiting." exit 1 fi MAIN=$MAINFILE fi # And finally concatenate Program.js echo "Writing $PROGRAM.js ..." cat $LIBS $COMPILED $INIT $MAIN > $PROGRAM.js echo "Done." if [ ! -z $CLOSUREFULL ]; then echo "Compiling $PROGRAM.js file using Google closure compiler." mv $PROGRAM.js $PROGRAM.js.original java -jar ~/compiler.jar $CLOSUREOPTS --js $PROGRAM.js.original --js_output_file $PROGRAM.js rm $PROGRAM.js.original echo "Done." fi