This patch should be applied to the
"ajuba-tclblend-contract-2000-08-01-branch".
The patch includes the following:
- fix bugs in tcl.lang.Notifier
- make TclBlend work with the Jacl shell
- add mutex for multi thread TclBlend initialization
- add (Win) makefile targets for testing TclBlend in JVM
This patch together with Mo's changes on removing the global lock and making
JNIEnv thread local make TclBlend almost thread safe. I say "almost"
because we still need to make one last change to the Java side of TclBlend
to make the Notifier class per Interp.
I outlined the various initialization paths the TclBlend can go through and
how the mutex protects these situations in the following document, item 3
and 4. Your comments and questions are welcome.
http://www-cs-students.stanford.edu/~jwu/blendchanges.txt
-- Jiang Wu
[EMAIL PROTECTED]
Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcljava/ChangeLog,v
retrieving revision 1.49.2.2
diff -b -u
-r1.49.2.2 ChangeLog
--- ChangeLog 2000/07/30 07:17:08 1.49.2.2
+++ ChangeLog
2000/08/05 23:25:45
@@ -1,3 +1,24 @@
+2000-08-03 Jiang Wu <[EMAIL PROTECTED]>
+
* src/tclblend/tcl/lang/Notifier.java:
+ Fix the following Notifier bugs:
+
- infinite loop when doOneEvent is called recursively
+ - synchronization
deadlock between serviceEvent and queueEvent
+ - deleteEvent() does not work
properly
+ * src/tclblend/tcl/lang/Util.java:
+ *
src/tclblend/tcl/lang/Interp.java:
+ Added the necessary methods for tcl.lang.Shell.
+ * src/native/javaCmd.c:
+ * src/native/javaInterp.c:
+ Added a global
mutex to protect TclBlend initialization from
+ multiple C or Java threads.
+ *
win/makefile.vc
+ Modified the Win makefile to build the Jacl Shell for tclblend.
+ Added a 'blendsh.install' target to install a 'blendsh.bat' that
+ can be
used to start a Java shell running TclBlend. Added a
+ 'test_jvmblend.exec' target
to run the TclBlend test suite using
+ JVM loading TclBlend.
+
+
2000-07-30 Mo
DeJong <[EMAIL PROTECTED]>
* src/native/java.h:
Index: src/native/javaCmd.c
===================================================================
RCS file:
/cvsroot/tcljava/src/native/javaCmd.c,v
retrieving revision 1.9.2.1
diff -b -u
-r1.9.2.1 javaCmd.c
--- javaCmd.c 2000/07/30 07:17:08 1.9.2.1
+++ javaCmd.c
2000/08/05 23:25:48
@@ -94,6 +94,22 @@
static int initialized_javaVM = 0;
/*
+ *
Declare a global mutex to protect the creation and initialization of the
+ * JVM from
mutiple threads. This mutex is used in conjunction with the
+ * 'initialized_javaVM'
flag. This mutex is used in javaCmd.c as well as
+ * javaInterp.c.
+ *
+ * FIXME:
don't want to use the flag TCL_THREADS explicitly. This may be
+ * better if in the
future the same TclBlend binary can be made to work with
+ * both threaded and
non-threaded Tcl libraries. For now, we will use accessor
+ * functions
lockJVMInitMutex() and unlockJVMInitMutex().
+ */
+TCL_DECLARE_MUTEX(javaVM_init_mutex)
+
+TCLBLEND_EXTERN void
LockJVMInitMutex();
+TCLBLEND_EXTERN void UnlockJVMInitMutex();
+
+/*
*
The following array contains the class names and jclass pointers for
* all of the
classes used by this module. It is used to initialize
* the java structure's jclass
slots.
@@ -242,6 +258,13 @@
fprintf(stderr, "Tcl Blend debug: CLASSPATH is
\"%s\"\n", getenv("CLASSPATH"));
#endif /* TCLBLEND_DEBUG */
+ /*
+ *
Calling JavaGetEnv() and JavaSetupJava() should be atomic because these
+ * two
functions may alter global data. Lock the JVM init mutex to create
+ * a critical
section.
+ */
+ LockJVMInitMutex();
+
env = JavaGetEnv(interp);
/*
@@ -254,6 +277,7 @@
fprintf(stderr, "Tcl Blend debug: JVM init failed in
javaCmd.c:\n");
#endif /* TCLBLEND_DEBUG */
+ UnlockJVMInitMutex();
return TCL_ERROR;
}
@@ -275,11 +299,18 @@
if (JavaSetupJava(env, interp)
!= TCL_OK) {
+ UnlockJVMInitMutex();
return TCL_ERROR;
}
}
/*
+ * End critical section. From this point on, the code is thread
+ *
specific.
+ */
+ UnlockJVMInitMutex();
+
+ /*
* Allocate a new
Interp instance to wrap this interpreter.
*/
@@ -405,7 +436,7 @@
}
/* FIXME : we need to put a mutex around this create JVM/attach step */
-
+ /*
^^^^^ -- no need for mutex here, use mutex in the caller function */
#ifdef
TCLBLEND_DEBUG
fprintf(stderr, "Tcl Blend debug: init 1.2 JVM in javaCmd.c\n");
#endif /* TCLBLEND_DEBUG */
@@ -573,7 +604,7 @@
}
/* FIXME: need to put a
mutex around this create/attach step */
-
+ /* ^^^^^ -- no need for mutex here, use
mutex in the caller function */
#ifdef TCLBLEND_DEBUG
fprintf(stderr, "Tcl Blend
debug: init 1.1 JVM in javaCmd.c: \n");
#endif /* TCLBLEND_DEBUG */
@@ -846,6 +877,7
@@
/* FIXME : we need to put a mutex around this test/set */
+ /* ^^^^^ --
no need for mutex here, use mutex in the caller function */
if
(initialized_javaVM) {
return TCL_OK;
}
@@ -1122,3 +1154,45 @@
return
buf;
}
+
+/*
+
*----------------------------------------------------------------------
+ *
+ *
LockJVMInitMutex --
+ *
+ * Acquire the 'init_javaVM_mutex'.
+ *
+ * Results:
+ *
None.
+ *
+ * Side effects:
+ * Maybe block the caller until the mutex is available.
+
*
+ *----------------------------------------------------------------------
+ */
+TCLBLEND_EXTERN void
+LockJVMInitMutex()
+{
+ Tcl_MutexLock(&javaVM_init_mutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlockJVMInitMutex --
+ *
+ * Release the 'init_javaVM_mutex'.
+ *
+ *
Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+
*----------------------------------------------------------------------
+ */
+TCLBLEND_EXTERN void
+UnlockJVMInitMutex()
+{
+
Tcl_MutexUnlock(&javaVM_init_mutex);
+}
Index: src/native/javaInterp.c
===================================================================
RCS file:
/cvsroot/tcljava/src/native/javaInterp.c,v
retrieving revision 1.7.2.1
diff -b -u
-r1.7.2.1 javaInterp.c
--- javaInterp.c 2000/07/30 07:17:09 1.7.2.1
+++
javaInterp.c 2000/08/05 23:25:51
@@ -28,6 +28,12 @@
} JavaTraceInfo;
/*
+ *
This file wants to use lockJVMInitMutex() and unlockJVMInitMutex().
+ */
+TCLBLEND_EXTERN void LockJVMInitMutex();
+TCLBLEND_EXTERN void
UnlockJVMInitMutex();
+
+/*
* Declaractions for functions used only in this file.
*/
@@ -102,6 +108,14 @@
Tcl_Interp *interp;
interp = Tcl_CreateInterp();
+
+ /*
+ * JavaSetupJava() should be executed atomically because it may
+ *
alter global data. Use the JVM init mutex to create a critical
+ * section.
+
*/
+ LockJVMInitMutex();
+
if (JavaSetupJava(env, interp) != TCL_OK) {
jclass err = (*env)->FindClass(env, "tcl/lang/TclRuntimeError");
if (err) {
@@ -112,6 +126,11 @@
} else {
*(Tcl_Interp**)&lvalue = interp;
}
+
+ /*
+ * End critical section.
+ */
+ UnlockJVMInitMutex();
return lvalue;
}
Index: src/tclblend/tcl/lang/Interp.java
===================================================================
RCS file:
/cvsroot/tcljava/src/tclblend/tcl/lang/Interp.java,v
retrieving revision 1.16
diff -b
-u -r1.16 Interp.java
--- Interp.java 2000/07/30 02:37:18 1.16
+++
Interp.java 2000/08/05 23:25:53
@@ -318,6 +318,37 @@
/*
*----------------------------------------------------------------------
*
+ * setVar
--
+ *
+ * Set the value of a variable.
+ *
+ * Results:
+ * Returns the
new value of the variable.
+ *
+ * Side effects:
+ * May trigger traces.
+ *
+
*----------------------------------------------------------------------
+ */
+
+public
final TclObject
+setVar(
+ String name, // Name of variable, array, or
array element
+ // to set.
+ String value, //
New value for variable.
+ int flags) // Various flags that
tell how to set value:
+ // any of GLOBAL_ONLY,
NAMESPACE_ONLY,
+ // APPEND_VALUE, LIST_ELEMENT, or
+
// LEAVE_ERR_MSG.
+throws
+ TclException
+{
+ return
setVar(name, TclString.newInstance(value), flags);
+}
+
+/*
+
*----------------------------------------------------------------------
+ *
* getVar
--
*
* Get the value of a variable.
@@ -819,8 +850,23 @@
throws
TclException // A standard Tcl exception.
{
+ // Append the script to the event
list by calling "history add <script>".
+ // We call the eval method with the
command of type TclObject, so that
+ // we don't have to deal with funny chars
("{}[]$\) in the script.
+
+ try {
+ TclObject cmd = TclList.newInstance();
+
TclList.append(this, cmd, TclString.newInstance("history"));
+
TclList.append(this, cmd, TclString.newInstance("add"));
+ TclList.append(this,
cmd, script);
+ eval(cmd, 0);
+ } catch (Exception e) {}
+
+ // Finally
evaluate the script.
+
+ eval(script, flags);
+
// FIXME : need native
implementation
- throw new TclRuntimeError("Not implemented yet.");
}
/*
@@
-846,8 +892,11 @@
throws
TclException
{
+ // use the source command to
evalFile
+ eval("source {" + s + "}", 0);
+
// FIXME : need implementation
-
throw new TclRuntimeError("Not implemented yet.");
+ // throw new
TclRuntimeError("Not implemented yet.");
}
/*
@@ -1031,6 +1080,42 @@
public
native void
addErrorInfo(
String message); // Message to add to
errorInfo
+
+/*
+
*----------------------------------------------------------------------
+ *
+ *
updateReturnInfo --
+ *
+ * This internal method is used by various parts of the
Jacl
+ * interpreter when a TclException of TCL.RETURN is received.
+ *
+ *
This method is provided here for compatibility with Jacl Interp class
+ *
methods. This method should never be reached in TclBlend.
+ *
+ * Results:
+ * The
return value is the true completion code to use for
+ * the Tcl procedure, instead
of TCL.RETURN. It's the same
+ * value that was given to the "return -code"
option.
+ *
+ * If an internal value of TCL.OK is returned, it means than the
+ *
caller of this method should ignore any TclException that it has
+ * just
received and continue execution.
+ *
+ * Side effects:
+ * The errorInfo and errorCode
variables may get modified.
+ *
+
*----------------------------------------------------------------------
+ */
+
+protected int
+updateReturnInfo()
+{
+ int code = TCL.ERROR;
+
+
setErrorCode(TclInteger.newInstance(code));
+ addErrorInfo("Unexpected call to
updateReturnInfo() in TclBlend.");
+ return code;
+}
/*
*----------------------------------------------------------------------
Index:
src/tclblend/tcl/lang/Notifier.java
===================================================================
RCS file:
/cvsroot/tcljava/src/tclblend/tcl/lang/Notifier.java,v
retrieving revision 1.2
diff -b
-u -r1.2 Notifier.java
--- Notifier.java 2000/01/25 03:42:25 1.2
+++
Notifier.java 2000/08/05 23:25:55
@@ -39,7 +39,7 @@
* interpreter (thread).
*/
-public class Notifier {
+public class Notifier implements EventDeleter {
private
static Notifier globalNotifier;
@@ -65,6 +65,13 @@
int refCount;
+// Mutex
used to protect concurrent access to the internals of this Notifier
+// object. For
example, the queueing and dequeueing of objects from the
+// event list.
+
+private
final Object notifierMutex = new Object();
+
+
/*
*----------------------------------------------------------------------
*
@@ -127,7
+134,7 @@
*
* Increment the reference count of the notifier. The notifier will
* be kept in the notifierTable (and alive) as long as its reference
- * count is
greater than zero.
+ * count is greater than zero. This method is concurrent safe.
*
* Results:
* None.
@@ -138,13 +145,16 @@
*----------------------------------------------------------------------
*/
-public
synchronized void
+public void
preserve()
{
+ synchronized (notifierMutex) {
if (refCount < 0) {
- throw new TclRuntimeError("Attempting to preserve a freed
Notifier");
+ throw new TclRuntimeError(
+ "Attempting to preserve
a freed Notifier");
}
++refCount;
+ }
}
/*
@@ -153,7 +163,8 @@
*
release --
*
* Decrement the reference count of the notifier. The notifier will
-
* be free when its refCount goes from one to zero.
+ * be free when its refCount
goes from one to zero. This method is
+ * concurrent safe.
*
* Results:
*
None.
@@ -165,9 +176,10 @@
*----------------------------------------------------------------------
*/
-public
synchronized void
+public void
release()
{
+ synchronized (notifierMutex) {
if ((refCount == 0) && (primaryThread != null)) {
throw new TclRuntimeError(
"Attempting to release a Notifier before it's preserved");
@@ -181,6 +193,7 @@
globalNotifier = null;
dispose();
}
+ }
}
/*
@@ -193,7
+206,8 @@
* Events inserted before the marker will be processed in
*
first-in-first-out order, but before any events inserted at
* the tail of the queue.
Events inserted at the head of the
- * queue will be processed in last-in-first-out
order.
+ * queue will be processed in last-in-first-out order. This method is
+ *
concurrent safe.
*
* Results:
* None.
@@ -206,7 +220,7 @@
*----------------------------------------------------------------------
*/
-public
synchronized void
+public void
queueEvent(
TclEvent evt, // The
event to put in the queue.
int position) // One of TCL.QUEUE_TAIL,
@@
-214,9 +228,9 @@
{
evt.notifier = this;
+ synchronized (notifierMutex) {
if (position == TCL.QUEUE_TAIL) {
// Append the event on the end of the
queue.
-
evt.next = null;
if (firstEvent == null) {
@@ -227,7 +241,6
@@
lastEvent = evt;
} else if (position == TCL.QUEUE_HEAD) {
// Push
the event on the head of the queue.
-
evt.next = firstEvent;
if (firstEvent
== null) {
lastEvent = evt;
@@ -236,7 +249,6 @@
} else if (position ==
TCL.QUEUE_MARK) {
// Insert the event after the current marker event and
advance
// the marker to the new event.
-
if (markerEvent == null) {
evt.next = firstEvent;
firstEvent = evt;
@@ -250,10 +262,10 @@
}
} else {
// Wrong flag.
-
throw new TclRuntimeError("wrong position
\"" + position +
"\", must be TCL.QUEUE_HEAD, TCL.QUEUE_TAIL or
TCL.QUEUE_MARK");
}
+ }
if (Thread.currentThread() != primaryThread) {
alertNotifier();
@@ -267,7 +279,8 @@
*
* Calls an EventDeleter for each
event in the queue and deletes
* those for which deleter.deleteEvent() returns
1. Events
- * for which the deleter returns 0 are left in the queue.
+ * for
which the deleter returns 0 are left in the queue. This
+ * method is
concurrent safe.
*
* Results:
* None.
@@ -278,13 +291,14 @@
*----------------------------------------------------------------------
*/
-public
synchronized void
+public void
deleteEvents(
EventDeleter deleter) // The
deleter that checks whether an event
// should be removed.
{
TclEvent evt, prev;
+ synchronized (notifierMutex) {
for (prev =
null, evt = firstEvent; evt != null; evt = evt.next) {
if
(deleter.deleteEvent(evt) == 1) {
if (firstEvent == evt) {
@@ -292,16
+306,49 @@
if (evt.next == null) {
lastEvent =
null;
}
+ if (markerEvent == evt) {
+
markerEvent = null;
+ }
} else {
prev.next
= evt.next;
+ if (evt.next == null) {
+ lastEvent =
prev;
}
if (markerEvent == evt) {
- markerEvent =
null;
+ markerEvent = prev;
+ }
}
}
else {
prev = evt;
}
}
+ }
+}
+
+/*
+
*----------------------------------------------------------------------
+ *
+ *
deleteEvent --
+ *
+ * This method is required for this class being an
EventDeleter.
+ * Checks the given event to see if it is already processed.
+ *
This method together with 'deleteEvents' are used by serviceEvent to
+ *
remove an event that is just processed in a concurrent safe manor.
+ *
+ * Results:
+
* Returns 1 if the given event is processed, 0 otherwise.
+ *
+ * Side effects:
+ *
None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+public int
+deleteEvent(
+ TclEvent evt) { // Check whether
this event should be removed.
+ return ((evt.isProcessing == true) &&
+
(evt.isProcessed == true)) ? 1 : 0;
}
/*
@@ -309,7 +356,8 @@
*
*
serviceEvent --
*
- * Process one event from the event queue.
+ * Process one
event from the event queue. This method is concurrent
+ * safe and can be reentered
recursively.
*
* Results:
* The return value is 1 if the procedure actually
found an event
@@ -322,8 +370,7 @@
*
*----------------------------------------------------------------------
*/
-
-synchronized int
+int
serviceEvent(
int flags) // Indicates
what events should be processed.
// May be any combination
of TCL.WINDOW_EVENTS
@@ -343,7 +390,8 @@
// Loop through all the events in the
queue until we find one
// that can actually be handled.
- for (evt =
firstEvent; evt != null; evt = evt.next) {
+ evt = null;
+ while ((evt =
getAvailableEvent(evt)) != null) {
// Call the handler for the event. If it
actually handles the
// event then free the storage for the event. There are
two
// tricky things here, both stemming from the fact that the event
@@ -357,37
+405,18 @@
// change almost arbitrarily while handling the event, so we
// can't depend on pointers found now still being valid when
// the
handler returns.
-
+ synchronized (evt) {
boolean b = evt.isProcessing;
evt.isProcessing = true;
if ((b == false) && (evt.processEvent(flags) != 0)) {
evt.isProcessed = true;
if (evt.needsNotify) {
-
synchronized(evt) {
evt.notifyAll();
}
- }
- if
(firstEvent == evt) {
- firstEvent = evt.next;
- if (evt.next
== null) {
- lastEvent = null;
- }
- if
(markerEvent == evt) {
- markerEvent = null;
- }
-
} else {
- for (prev = firstEvent; prev.next != evt; prev = prev.next) {
-
// Empty loop body.
- }
- prev.next = evt.next;
-
if (evt.next == null) {
- lastEvent = prev;
- }
-
if (markerEvent == evt) {
- markerEvent = prev;
-
}
- }
+
+ deleteEvents(this); // this takes care
of deletion of the
+ // just processed event
return 1;
} else {
// The event wasn't actually handled, so we have to
@@ -395,19 +424,59 @@
// attempted again.
evt.isProcessing = b;
-
}
// The handler for this event asked to defer it. Just go on to
- //
the next event.
-
- continue;
+ // the next event. we will try to find
another event to
+ // process when the while loop continues
+ }
+ }
}
+
return 0;
}
/*
*----------------------------------------------------------------------
*
+ *
getAvailableEvent --
+ *
+ * Search through the internal event list to find the
first event
+ * that is has not being processed AND the event is not equal to the
given
+ * 'skipEvent'. This method is concurrent safe.
+ *
+ * Results:
+
* The return value is a pointer to the first found event that can be
+ *
processed. If no event is found, this method returns null.
+ *
+ * Side effects:
+ *
This method synchronizes on the 'notifierMutex', which will block any
+ * other
thread from adding or removing events from the event queue.
+ *
+
*----------------------------------------------------------------------
+ */
+
+private TclEvent
+getAvailableEvent(
+ TclEvent skipEvent) // Indicates that
the given event should not
+ // be returned. This argument
can be null.
+{
+ TclEvent evt;
+
+ synchronized(notifierMutex) {
+ for
(evt = firstEvent ; evt != null ; evt = evt.next) {
+ if ((evt.isProcessing
== false) &&
+ (evt.isProcessed == false) &&
+ (evt !=
skipEvent)) {
+ return evt;
+ }
+ }
+ }
+ return null;
+}
+
+/*
+
*----------------------------------------------------------------------
+ *
*
doOneEvent --
*
* Process a single event of some sort. If there's no work to
@@ -503,7 +572,8 @@
*
* hasEvents --
*
- * Check to see if there are events
waiting to be processed.
+ * Check to see if there are events waiting to be
processed. This
+ * method is concurrent safe.
*
* Results:
* Returns true
if there are events on the Notifier queue.
@@ -514,10 +584,12 @@
*----------------------------------------------------------------------
*/
-private final synchronized boolean
+private final boolean
hasEvents()
{
-
return (firstEvent != null);
+ boolean result = (getAvailableEvent(null) != null);
+
+ return result;
}
} // end Notifier
Index: src/tclblend/tcl/lang/Util.java
===================================================================
RCS file:
/cvsroot/tcljava/src/tclblend/tcl/lang/Util.java,v
retrieving revision 1.1.1.1
diff -b
-u -r1.1.1.1 Util.java
--- Util.java 1998/10/14 21:09:10 1.1.1.1
+++ Util.java
2000/08/05 23:25:55
@@ -59,6 +59,80 @@
/*
*----------------------------------------------------------------------
*
+ * isUnix
--
+ *
+ * Returns true if running on a Unix platform.
+ *
+ * Results:
+ *
Returns a boolean.
+ *
+ * Side effects:
+ * None.
+ *
+
*----------------------------------------------------------------------
+ */
+
+final
static boolean
+isUnix() {
+ if (isMac() || isWindows()) {
+ return false;
+
}
+ return true;
+}
+
+/*
+
*----------------------------------------------------------------------
+ *
+ * isMac
--
+ *
+ * Returns true if running on a Mac platform.
+ *
+ * Results:
+ * Returns
a boolean.
+ *
+ * Side effects:
+ * None.
+ *
+
*----------------------------------------------------------------------
+ */
+
+final
static boolean
+isMac() {
+ String os = System.getProperty("os.name");
+ if
(os.toLowerCase().startsWith("mac")) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ *
isWindows --
+ *
+ * Returns true if running on a Windows platform.
+ *
+ * Results:
+
* Returns a boolean.
+ *
+ * Side effects:
+ * None.
+ *
+
*----------------------------------------------------------------------
+ */
+
+final
static boolean
+isWindows() {
+ String os = System.getProperty("os.name");
+ if
(os.toLowerCase().startsWith("win")) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ *----------------------------------------------------------------------
+
*
* printDouble --
*
* Returns the string form of a double. The exact
formatting
Index: win/makefile.vc
===================================================================
RCS file:
/cvsroot/tcljava/win/makefile.vc,v
retrieving revision 1.27.2.1
diff -b -u -r1.27.2.1
makefile.vc
--- makefile.vc 2000/07/30 04:33:15 1.27.2.1
+++ makefile.vc
2000/08/05 23:26:00
@@ -350,6 +350,22 @@
exit
<<
+test_jvmblend.exec: test_tclblend.check
+ cd $(BUILD_DIR)
+ set
CLASSPATH=$(TEST_CLASSPATH)
+ set
PATH=$(TCL_DIR)\bin;$(BUILD_DIR);$(JAVA_RUNTIME_LIBS);$(PATH)
+ set
TCL_LIBRARY=$(TCL_DIR)\lib\tcl8.3
+ $(JAVA) $(JAVA_FLAGS) \
+
-DBUILD_DIR=$(BUILD_DIR) -DJAVA=$(JAVA) tcl.lang.Shell <<
+ set
auto_path [concat [pwd] [set auto_path]]
+ cd {$(TOP_DIR)\tests}
+
puts "pwd is [pwd]"
+ puts "CLASSPATH is [set
env(CLASSPATH)]"
+ puts "auto_path is [set auto_path]"
+
source all
+ puts "Tests done."
+ exit
+<<
#----------------------------------------------------------------------
# Running the
Jacl test suite
@@ -567,7 +583,8 @@
cd $(TCLBLEND_SRC_DIR)
set
CLASSPATH=$(TCLBLEND_CLASSPATH)
$(JAVAC) $(JAVAC_FLAGS) \
- -d
$(TCLBLEND_BUILD_DIR) tcl\lang\*.java
+ -d $(TCLBLEND_BUILD_DIR)
tcl\lang\*.java \
+ $(JACL_SRC_DIR)\tcl\lang\Shell.java
tclblend.jar:
tclblend.build
cd $(TCLBLEND_BUILD_DIR)
@@ -601,6 +618,19 @@
-@mkdir
$(TCL_DIR)\lib\xputils
-@$(COPYDIR) $(BUILD_DIR)\xputils
$(TCL_DIR)\lib\xputils
+#
+# temp target for installing a blendsh.bat
+#
+blendsh.install:
+ -@echo Installing blendsh.bat in $(BIN_INSTALL_DIR)
+
-@del $(BIN_INSTALL_DIR)\blendsh.bat
+ -@echo REM >> $(BIN_INSTALL_DIR)\blendsh.bat
+
-@echo REM This batch file starts the Jacl/TclBlend shell with a JVM >>
$(BIN_INSTALL_DIR)\blendsh.bat
+ -@echo REM >> $(BIN_INSTALL_DIR)\blendsh.bat
+
-@echo set TCL_LIBRARY=$(TCL_DIR)\lib\tcl8.3>> $(BIN_INSTALL_DIR)\blendsh.bat
+
-@echo set PATH=$(BIN_INSTALL_DIR);$(LIB_INSTALL_DIR);$(JAVA_RUNTIME_LIBS) >>
$(BIN_INSTALL_DIR)\blendsh.bat
+ -@echo set
CLASSPATH=$(JAVA_CLASSPATH);$(LIB_INSTALL_DIR)\tclblend.jar;$(LIB_INSTALL_DIR)\tcljava.jar
>> $(BIN_INSTALL_DIR)\blendsh.bat
+ -@echo $(JAVA) -cp %CLASSPATH%
tcl.lang.Shell >> $(BIN_INSTALL_DIR)\blendsh.bat
#
# Check to make sure tclblend
is built
@@ -639,6 +669,7 @@
tcl.lang.Notifier \
tcl.lang.TclList \
tcl.lang.TimerHandler \
+ tcl.lang.Shell \
tcl.lang.Util