com-lihaoyi / Ammonite

Scala Scripting
http://ammonite.io
MIT License
2.61k stars 371 forks source link

`sbt run` hangs while using ammonite as a library #1116

Open mushtaq opened 4 years ago

mushtaq commented 4 years ago

steps

Clone: https://github.com/shivanithorat/ammonite-simple run this: sbt run

problem

@ prompt of ammonite appears, but does not allow to enter any input ctrl-c is unable to terminate the process, it has to be killed.

notes

sbt version: 1.4.0-RC1 ammonite version: 2.2.0 The main program is just this:

object Main {
  def main(args: Array[String]): Unit = {
    ammonite.Main().run()
  }
}

related

Possibly, due to conflict in JLine3 of ammonite repl and that of sbt shell as pointed out by @eatkins in https://github.com/sbt/sbt/issues/5856

eatkins commented 4 years ago

I was mistaken about a JLine3 conflict. With some fixes on the sbt side, the ammonite repl still works as it did before.

That being said, there is still an issue with how ammonite works within an sbt session. It seems to always shell out to run stty commands. This works ok when using ammonite inside of a normal sbt console session. However, sbt 1.4.0 adds a new thin client to sbt so that the user can quickly connect to a running server without having to sit through the slow sbt build loading. The ui for the thin client is rendered by the server and there is a protocol for exchanging input bytes from the thin client to the server and output bytes from the server to the client. As a result, the tty for the thin client is not necessarily the same tty as for the server so the stty commands are being run on the wrong process. This actually still kind of works as long as the server has an attached tty. However, if there is no running server, the thin client starts up a server and after loading, the server closes its input and output streams. The ammonite stty commands continue working for as long as the initial thin client process that launched the server exists. Once that client process exits, however, subsequent stty commands fail and the ammonite repl fails and starts repeating an infinite loop of failed stty commands:

stty: tcsetattr: Input/output error                                 
java.lang.RuntimeException: Nonzero exit value: 1                   
  scala.sys.process.ProcessBuilderImpl$AbstractBuilder.slurp(ProcessBuilderImpl.scala:161)                                              
  scala.sys.process.ProcessBuilderImpl$AbstractBuilder.$bang$bang(ProcessBuilderImpl.scala:118)                                         
  ammonite.terminal.TTY$.stty(Utils.scala:103)                      
  ammonite.terminal.TTY$.withSttyOverride(Utils.scala:119)          
  ammonite.terminal.Terminal$.readLine(Terminal.scala:41)           
  ammonite.repl.AmmoniteFrontEnd.readLine(AmmoniteFrontEnd.scala:133)                                                                   
  ammonite.repl.AmmoniteFrontEnd.action(AmmoniteFrontEnd.scala:25)  
  ammonite.repl.Repl.$anonfun$action$4(Repl.scala:192)              
  ammonite.repl.Scoped.$anonfun$flatMap$1(Signaller.scala:45)       
  ammonite.repl.Signaller.apply(Signaller.scala:28)                 
  ammonite.repl.Scoped.flatMap(Signaller.scala:45)                  
  ammonite.repl.Scoped.flatMap$(Signaller.scala:45)                 
  ammonite.repl.Signaller.flatMap(Signaller.scala:16)               
  ammonite.repl.Repl.$anonfun$action$2(Repl.scala:176)              
  ammonite.util.Catching.flatMap(Res.scala:115)                     
  ammonite.repl.Repl.action(Repl.scala:168)                         
  ammonite.repl.Repl.loop$1(Repl.scala:208)                         
  ammonite.repl.Repl.run(Repl.scala:223)                            
  ammonite.Main.$anonfun$run$1(Main.scala:223)                      
  scala.Option.getOrElse(Option.scala:201)                          
  ammonite.Main.run(Main.scala:211)                                 
  Main$.main(Main.scala:6)                                          
  Main.main(Main.scala)                                             
  jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)                                                                  
  jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)                                                
  jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)                                        
  java.lang.reflect.Method.invoke(Method.java:566)                  
  sbt.Run.invokeMain(Run.scala:133)                                 
  sbt.Run.execute$1(Run.scala:82)                                   
  sbt.Run.$anonfun$runWithLoader$5(Run.scala:110)                   
  scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)                                                                
  sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:17)        
  sbt.TrapExit$App.run(TrapExit.scala:258)                          
  java.lang.Thread.run(Thread.java:834)                             

I think ammonite would work with the thin client if there was just a way to skip the stty commands. sbt can put itself into raw mode with echo off (https://github.com/sbt/sbt/pull/5874/commits/ddb626a9bed638faa461eba24839273f2ca33699) before entering the ammonite console, so it's not necessary to modify the tty.

beyondpie commented 2 months ago

@mushtaq Thank you! By using your example, I can now invoke amm in my sbt.