Gnu Parallel for Makefile goodness.

Apr 27, 2016 01:00 · 381 words · 2 minutes read

With projects I like to have a single make run to kick off the dev project. So when recently building a Vagrant box for a project I wanted my README Getting Started section to just contain:

vagrant up && vagrant ssh -c "make run" 

Now unfortunately, in my situation, make run needed to gracefully start and stop multiple processes. I am running a development app server, Webpack for the JavaScript and Gulp for the SASS (because Webpack sucks with SASS).

Now naively make run could just have done:

python manage.py runserver & gulp watch & webpack-dev-server

But when we Ctrl+C to close, it will only kill the last process as everything else was sent to the background. Not ideal.

Enter GNU parallel. installed with sudo apt-get install parallel on Ubuntu. The following command:

(echo python manage.py runserver; echo gulp watch; echo webpack-dev-server) | parallel

Will run all processes in parallel and then kills them all on a single Ctrl+C.

Perfect. Well almost. My host only had two CPUS, and parallel by default seems to execute at most ${CPU_COUNT} processes at a time. As I had N+1 processes it was waiting for a process to finish before kicking off the third and final process.

parallel -P 0 solved this issue, it tells parallel not to limit on CPU count and to just run everything at once.

Better. But I am still not getting anything in stdout.

By default parallel waits for a process to finish before dumping any output to stdout. For watch processes where you care about the output immediately and it will never finish, this is not ideal.

parallel --ungroup is what we’re missing. Instead of waiting and grouping a commands stdout together, --ungroup tells it to just write straight away and not wait for completion.

Almost there. But I am getting interspersed output on a per line basis.

What we’re missing is parallel --line-buffer, which buffers output from each process on a per line basis before writing to stdout.

So the final make run executes:

(echo python manage.py runserver; echo gulp watch; echo webpack-dev-server) | parallel -P 0 --line-buffer --ungroup

It gracefully starts and stops all 3 processes in a single shell.

My Getting Started section can now just read:

vagrant up && vagrant ssh -c "make run"

Simple.