diff options
-rw-r--r-- | nailgun_launcher/NailgunLauncher.java | 19 | ||||
-rw-r--r-- | nailgun_launcher/ThreadLocalOutputStream.java | 30 | ||||
-rw-r--r-- | stage1/Stage1Lib.scala | 35 |
3 files changed, 77 insertions, 7 deletions
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java index 37bfa06..b1daf2a 100644 --- a/nailgun_launcher/NailgunLauncher.java +++ b/nailgun_launcher/NailgunLauncher.java @@ -59,7 +59,24 @@ public class NailgunLauncher{ installProxySettings(); String[] diff = args[0].split("\\."); long start = _start - (Long.parseLong(diff[0]) * 1000L) - Long.parseLong(diff[1]); - + + // if nailgun didn't install it's threadLocal stdout/err replacements, install CBT's. + // this hack allows to later swap out System.out/err while still affecting things like + // scala.Console, which captured them at startup + try{ + System.out.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream + assert(System.out.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream"); + } catch( NoSuchFieldException e ){ + System.setOut( new PrintStream(new ThreadLocalOutputStream(System.out)) ); + } + try{ + System.err.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream + assert(System.out.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream"); + } catch( NoSuchFieldException e ){ + System.setErr( new PrintStream(new ThreadLocalOutputStream(System.err)) ); + } + // --------------------- + _assert(System.getenv("CBT_HOME") != null, "environment variable CBT_HOME not defined"); String CBT_HOME = System.getenv("CBT_HOME"); String cache = CBT_HOME + "/cache/"; diff --git a/nailgun_launcher/ThreadLocalOutputStream.java b/nailgun_launcher/ThreadLocalOutputStream.java new file mode 100644 index 0000000..c12b775 --- /dev/null +++ b/nailgun_launcher/ThreadLocalOutputStream.java @@ -0,0 +1,30 @@ +package cbt; +import java.io.*; + +public class ThreadLocalOutputStream extends OutputStream{ + final public ThreadLocal<OutputStream> threadLocal; + final private OutputStream initialValue; + + public ThreadLocalOutputStream( OutputStream initialValue ){ + this.initialValue = initialValue; + threadLocal = new ThreadLocal<OutputStream>() { + @Override protected OutputStream initialValue() { + return ThreadLocalOutputStream.this.initialValue; + } + }; + } + + public OutputStream get(){ + return threadLocal.get(); + } + + public void set( OutputStream outputStream ){ + threadLocal.set( outputStream ); + } + + public void write( int b ) throws IOException{ + // after implementing this I realized NailgunLauncher uses the same hack, + // so probably this is not a problem performance + get().write(b); + } +} diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 68648fe..273b9af 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -302,13 +302,36 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ } } def redirectOutToErr[T](code: => T): T = { - val oldOut = System.out - try{ - System.setOut(System.err) - code - } finally{ - System.setOut(oldOut) + val ( out, err ) = try{ + // trying nailgun's System.our/err wrapper + val field = System.out.getClass.getDeclaredField("streams") + assert(System.out.getClass.getName == "com.martiansoftware.nailgun.ThreadLocalPrintStream") + assert(System.err.getClass.getName == "com.martiansoftware.nailgun.ThreadLocalPrintStream") + field.setAccessible(true) + val out = field.get(System.out).asInstanceOf[ThreadLocal[PrintStream]] + val err = field.get(System.err).asInstanceOf[ThreadLocal[PrintStream]] + ( out, err ) + } catch { + case e: NoSuchFieldException => + // trying cbt's System.our/err wrapper + val field = classOf[FilterOutputStream].getDeclaredField("out") + field.setAccessible(true) + val outStream = field.get(System.out) + val errStream = field.get(System.err) + assert(outStream.getClass.getName == "cbt.ThreadLocalOutputStream") + assert(errStream.getClass.getName == "cbt.ThreadLocalOutputStream") + val field2 = outStream.getClass.getDeclaredField("threadLocal") + field2.setAccessible(true) + val out = field2.get(outStream).asInstanceOf[ThreadLocal[PrintStream]] + val err = field2.get(errStream).asInstanceOf[ThreadLocal[PrintStream]] + ( out, err ) } + + val oldOut: PrintStream = out.get + out.set( err.get: PrintStream ) + val res = code + out.set( oldOut ) + res } def trapExitCode( code: => ExitCode ): ExitCode = { |