sublimehq / sublime_text

Issue tracker for Sublime Text
https://www.sublimetext.com
814 stars 40 forks source link

build output doesn't flush on new line when printing to stdout #6048

Open ryuukk opened 1 year ago

ryuukk commented 1 year ago

Description of the bug

build's output never print what's being printed to stdout despite it being line buffered

     Normally all files are block buffered.  If a stream refers to a
       terminal (as stdout normally does), it is line buffered.  The
       standard error stream stderr is always unbuffered by default.

https://man7.org/linux/man-pages/man3/setbuf.3.html#DESCRIPTION

Steps to reproduce

  1. mimic a long sequence printf to stdout with occasional \n in your favorite language
  2. point to that program in your build's cmd

Expected behavior

Every-time it hits a new line, it should print the content

Actual behavior

It never print the content until the program exists

Sublime Text build number

4143

Operating system & version

Linux x64

(Linux) Desktop environment and/or window manager

xfce

Additional information

No response

OpenGL context information

No response

deathaxe commented 1 year ago

ST starts 2 threads to listen for data from stdout and stderr while build process is running and prints any content whenever it receives something.

I am not very confident about whether a caller can force a callee to behave in special ways beyond disabling stdout buffers by setting bufsize = 0, but I'd argue it's the callee's responsibility to flush stdout for it to be sent to a caller.

To check that, I did the following tests. The 3rd one runs python in buffered mode causing the issue described by OP, because ST does not receive anything via stdout until process has finished.

  1. Running a shell script tick.sh via ShellScript's default build system (using MingW's bash on Windows) or via WslBuild package's build system using WSL2.

    #!/usr/bin/env bash
    
    tick=1
    
    while [ $tick -le 5 ] ; do
    echo "Hello $tick"
    ((tick++))
    sleep 5
    done

    "Hello $tick" is printed to build output immediatelly every 5 secs each.

  2. Running a python script tick.py

    from time import sleep
    
    for i in range(5):
    print(f"Hello tick {i}")
    sleep(5)

    using

    {
    "shell_cmd": "python -u \"$file\"",
    "selector": "source.python",
    }

    "Hello tick ${i}" is printed to build output immediatelly every 5 secs each.

  3. Running a python script tick.py without -u flag (stdout is now buffered by python).

    {
    "shell_cmd": "python \"$file\"",
    "selector": "source.python",
    }

    Python buffers stdout and thus ST receives (and prints) content not before script has finished.

  4. Running a python script _tick_forceflush.py

    from time import sleep
    from sys import stdout
    
    for i in range(5):
    print(f"Hello tick {i}")
    stdout.flush()
    sleep(5)

    using python in buffered mode (without -u)

    {
    "shell_cmd": "python \"$file\"",
    "selector": "source.python",
    }

    "Hello tick ${i}" is printed to build output immediatelly every 5 secs each, as the script forces stdout to be flushed.