Bazel run closed stdin

Bazel is a build tool which coordinates builds and runs tests.

Recently I wrote some code as follow:

#include
#include

#include
#include

#include

int kbhit() {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); // STDIN_FILENO is 0
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}

int main(int argc, char const *argv[]) {
char buf[1001];
memset(buf, 0, sizeof(char) * 1001);
while (true) {
if (kbhit()) {
if(nullptr != fgets(buf, 1000, stdin)) {
int n = strlen(buf);
printf("In:[%d][%s]\n", n, buf);
}
} else {
usleep(200 * 1000); // sleep 200 ms
}
}
}

This is a simple code which is used to read stdin when some input stream comes, or process something else (in usleep section).

And then, I use

bazel run //path/to/my:code

It always jump to stdin but reads nothing.

With ONE day’s searching, I finally found this introduction

Bazel closes stdin, so you can’t use bazel run if you want to start an interactive program or pipe data to it.

————- Updated ————–

@pylipp‘s advice was super helpful. We can simply download this bazel-run.sh and start multiple processes at the same time. The stdin will not be closed.

However, another problem comes. Since we have no bash auto complication. This makes the command much hard than bazel. I make a few modification as follow:

#!/bin/bash
#
# Copyright 2016 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
USAGE='bexec [...] [ -- []... ]'
DESCRIPTION='
Builds and runs the command generated by "bazel run" in the calling
terminal. The command is run as a grandchild of the current shell,
not from the Bazel server. Therefore, the program will have a controlling terminal, and
the Bazel lock is released before running the command.'

function usage() {
echo "$USAGE" "$DESCRIPTION" >&2
}

function die() {
echo "$1"
exit 1
}

function cleanup() {
rm "$runcmd"
}

[ $# -gt 1 ] || { usage; exit 1; }

# echo "shift..:" $1
[ $1 == "usage" ] && { usage; exit 0; }
shift

runcmd="$(mktemp /tmp/bazel-run.XXXXXX)" || die "Could not create tmp file"
trap "cleanup" EXIT

bazel run --script_path="$runcmd" "$@" || exit $?
[ -x "$runcmd" ] || die "File $runcmd not executable"

"$runcmd"

Save the above script as ‘bexec’ and placed it in somewhere.

And then, append the follow line into ~/.bash_profile

_bexec__complete() {
local cur=${COMP_WORDS[COMP_CWORD]}
if [ $COMP_CWORD -gt 1 ]; then
if [ ${COMP_WORDS[1]} == "run" ]; then
if _cmd__chk _bazel__to_compreply && _cmd__chk _bazel__complete_stdout; then
_bazel__to_compreply "$(_bazel__complete_stdout)"
fi
else
local cands=$(echo "" | tr " " "\n")
COMPREPLY=( $(compgen -W "$cands" -- ${COMP_WORDS[COMP_CWORD]}))
fi
else
local cands=$(echo "run usage" | tr " " "\n")
COMPREPLY=( $(compgen -W "$cands" -- ${COMP_WORDS[COMP_CWORD]}))
fi
}

complete -F _bexec__complete -o nospace "bexec"

This script forwarded the result of bazel.

$ bexec run //examples/cxx:hello_world
Starting local Bazel server and connecting to it...
..........
(01:14:50) INFO: Current date is 2018-05-15
(01:14:51) INFO: Analysed target //examples/cxx:hello_world (13 packages loaded).
(01:14:51) INFO: Found 1 target...
Target //examples/cxx:hello_world up-to-date:
bazel-bin/examples/cxx/hello_world
(01:14:52) INFO: Elapsed time: 2.208s, Critical Path: 0.03s
(01:14:52) INFO: 0 processes.
(01:14:52) INFO: Build completed successfully, 1 total action

I0515 01:14:52.128720 3013522304 hello_world.cc:62] This container is palindrome
I0515 01:14:52.129794 3013522304 hello_world.cc:68] This container is palindrome
I0515 01:14:52.129806 3013522304 hello_world.cc:75] This container is palindrome
I0515 01:14:52.129813 3013522304 hello_world.cc:84] This container is palindrome
I0515 01:14:52.129820 3013522304 hello_world.cc:89] This container is palindrome
I0515 01:14:52.129827 3013522304 hello_world.cc:94] This container is palindrome
I0515 01:14:52.129833 3013522304 hello_world.cc:99] This container is palindrome
I0515 01:14:52.129839 3013522304 hello_world.cc:105] This container is palindrome
I0515 01:14:52.129847 3013522304 hello_world.cc:111] This C-string is palindrome
I0515 01:14:52.129860 3013522304 hello_world_lib.cc:32] examples/cxx/hello_world/hello_world_lib.cc#32 update: 2 <- 1
I0515 01:14:52.129854 3013522304 hello_world.cc:133] Hello, World! ~~~2
I0515 01:14:52.129873 3013522304 hello_world.cc:118] Hello, World! for loop at 11th
I0515 01:14:52.129880 3013522304 hello_world.cc:118] Hello, World! for loop at 21th
I0515 01:14:52.129887 3013522304 hello_world.cc:118] Hello, World! for loop at 31th
I0515 01:14:52.129894 3013522304 hello_world.cc:118] Hello, World! for loop at 41th
I0515 01:14:52.129901 3013522304 hello_world.cc:118] Hello, World! for loop at 51th
I0515 01:14:52.129909 3013522304 hello_world.cc:118] Hello, World! for loop at 61th
I0515 01:14:52.129915 3013522304 hello_world.cc:118] Hello, World! for loop at 71th
I0515 01:14:52.129922 3013522304 hello_world.cc:118] Hello, World! for loop at 81th
I0515 01:14:52.129930 3013522304 hello_world.cc:118] Hello, World! for loop at 91th
I0515 01:14:52.129936 3013522304 hello_world.cc:137] hello, info@glog
W0515 01:14:52.129943 3013522304 hello_world.cc:138] hello, warning@glog
E0515 01:14:52.130137 3013522304 hello_world.cc:139] hello, error@glog