# Spawn lib choice

**URL:** https://forum.ada-lang.io/t/spawn-lib-choice/1467
**Category:** General
**Tags:** spawn, posix, expect
**Created:** [November 13, 2024, 1:39pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467 "2024-11-13T13:39:05Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [November 13, 2024, 1:39pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/1 "2024-11-13T13:39:05Z")

</div>

Hi there,

I’m looking for a more sophisticated alternative to GNAT.OS\_Lib.Spawn to add features to [bbt](https://github.com/LionelDraghi/bbt).  
I want to feed the process with input, check outputs, ideally separately the standard and the error output, and set environment variables.  
I’m not interested at that point in running in // a bunch of processes.  
Portability on usual native platforms (Linux, Windows and MacOS) is fine.

I found [GNAT.Expect](https://www.adacore.com/gems/gem-54) very tempting, but error output doesn’t seems to be separated from normal output, and there is no env variable manipulation, as far as I see.

`Alire search spawn` (and posix and florist) gave me several results applicable to my case, but Maxim’s Spawn seems to be the only perfect fit.

Before switching to Spawn, I was curious : have you any experience to share with those more or less thick binding to the OS, any advice?

Thanks,  
Lionel

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [November 13, 2024, 1:55pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/2 "2024-11-13T13:55:50Z")

</div>

It is a complicated issue. Here is an implementation based on glib: [GTKAda contributions](http://www.dmitry-kazakov.de/ada/gtkada_contributions.htm#10)

---

<div class="post-metadata">

### Author: ![OneWingedShark](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/onewingedshark/32/305_2.png) [@OneWingedShark](https://forum.ada-lang.io/u/OneWingedShark)
#### Post date: [November 13, 2024, 7:01pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/3 "2024-11-13T19:01:29Z")

</div>

TBH, I try to keep things in standard Ada; this keeps things **very** portable — for something that is very system dependent, the standard way to do it is to have the system-dependencies collected and abstracted so that the rest of the program “has no idea” about what system it’s running on. (This is usually accomplished by using `separate` in implementations, selecting the appropriate implementation for the OS via the build-system.)

> [@LionelDraghi](#):
>
> I want to feed the process with input, check outputs, ideally separately the standard and the error output, and set environment variables.

Ok, so what you’re going to want to do is have three (at least) files — in `Ada.Text_IO` there’s three standard files/streams: Input, Output, and Error.

> **procedure** Set\_Input (File : **in** File\_Type);  
> **procedure** Set\_Output(File : **in** File\_Type);  
> **procedure** Set\_Error (File : **in** File\_Type);
> 
> **function** Standard\_Input **return** File\_Type;  
> **function** Standard\_Output **return** File\_Type;  
> **function** Standard\_Error **return** File\_Type;
> 
> **function** Current\_Input **return** File\_Type;  
> **function** Current\_Output **return** File\_Type;  
> **function** Current\_Error **return** File\_Type;

Depending on what functionality you want, is whether or not to use these; if, for example, there’s some reasonable expectatoin for foreign-language then using the interface-abstraction of EVIL’s file-type (an auto-closing, trivially interchangeable [[Wide\_]Wide\_]Character/String text-file, with easy stream-access) would be appropriate.

As for “spawn”, I would counsel asking several questions first, to aid in the design:

1. Are these disparate, singular items that I can do in Ada, that need to work together somehow?  
Use `Task`.
2. Are these a series of _doing the same thing_ tasks?  
Use `Task type`.
3. Are these processes that have the same sort of “shape”?  
Use `Task interface`.
4. Are these processes that are interfacing with the OS-functionality, have the same “shape”/interface, and are integral to the system/subsystem?  
Use task interface + separate and/or generics.
5. Are the above too much like “hardcoding” and ultimate flexibility is needed?  
Then use “spawn”.

I would highly recommend against jumping straight to spawning OS processes for most programs, and if you do have to drop down to that level, I would recommend having some universal/abstracted interface such that “the rest of the program” simply cannot distinguish what OS it’s being run on — is _is_ upfront work, yes; but it does make things much easier to maintain.

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [November 13, 2024, 7:41pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/4 "2024-11-13T19:41:17Z")

</div>

Usually spawn is used for running an application from Ada. As an example consider spawning gprbuild to compile and link something and show the output and error messages etc.

---

<div class="post-metadata">

### Author: ![OneWingedShark](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/onewingedshark/32/305_2.png) [@OneWingedShark](https://forum.ada-lang.io/u/OneWingedShark)
#### Post date: [November 13, 2024, 8:12pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/5 "2024-11-13T20:12:28Z")

</div>

Sure, but some people reach for _“let me use the command-line”_-style spawning to build up a Frankensteinian mismash of a program. (I’ve seen it a couple times with large PHP programs.)

Unix/linux also seem prone to this, and the ref to `GNAT.Lib_Spawn` and posix/florist raised that warning-flag in my mind.

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [November 13, 2024, 8:22pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/6 "2024-11-13T20:22:20Z")

</div>

Yes, this one of many instances of UNIX idiotism.

As a seasoned conspiracy theorist let me ask, do you know the true reason why the Mac mouse had only one button? Because UNIX started a separate process for each mouse button! 🤣

---

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [November 13, 2024, 8:40pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/7 "2024-11-13T20:40:39Z")

</div>

@OneWingedShark, the function of bbt is to run an external app, and check the behavior in black box mode, thanks to inputs and outputs.

I’m afraid there’s no way to avoid spawning here!

---

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [November 13, 2024, 8:46pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/8 "2024-11-13T20:46:51Z")

</div>

Thanks for the ref Dmitry.

Yes it’s complex, and I don’t want to loose my time on this topic, that’s why I’m going to use an existing interface.

---

<div class="post-metadata">

### Author: ![OneWingedShark](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/onewingedshark/32/305_2.png) [@OneWingedShark](https://forum.ada-lang.io/u/OneWingedShark)
#### Post date: [November 13, 2024, 11:56pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/9 "2024-11-13T23:56:28Z")

</div>

> [@dmitry-kazakov](#):
>
> Yes, this one of many instances of UNIX idiotism.
> 
> As a seasoned conspiracy theorist let me ask, do you know the true reason why the Mac mouse had only one button? Because UNIX started a separate process for each mouse button! 🤣

LOL / Ouch.

> [@LionelDraghi](#):
>
> @OneWingedShark, the function of bbt is to run an external app, and check the behavior in black box mode, thanks to inputs and outputs.
> 
> I’m afraid there’s no way to avoid spawning here!

In this particular case, I would recommend abstracting _ **everything** _ that is relevant:

1. A “path”, into a `private`, possibly `limited` type.
2. An “environment” into a `String —> String` map.
3. A process-execution into a synchronized interface, possibly with discriminants for the (a) application, (b) parameters and possibly (c) optional [nullable access] environment.

IOW, consider the whole of the “thing to execute” as a high-level abstraction, and use the type-system to implement _that_, then build upon that as the “what’s expected”.

As I’ve said elsewhere: the best way to use Ada is to use the type-system to descrube the problem, and then write the solution in _that_.

---

<div class="post-metadata">

### Author: ![ebriot](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/ebriot/32/156_2.png) [@ebriot](https://forum.ada-lang.io/u/ebriot)
#### Post date: [November 14, 2024, 8:10am UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/10 "2024-11-14T08:10:43Z")

</div>

Depending on the applications you are spawning, you might also have to care about creating pseudo-terminals (tty), or process groups. These make things even more complex.  
So in addition to GNAT.Expect, you should also consider using GNAT.Expect.TTY.

Also consider the `Err_To_Out` parameter in GNAT.Expect.Non\_Blocking\_Spawn, if you need to separate errors from standard output.

---

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [November 14, 2024, 5:00pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/11 "2024-11-14T17:00:11Z")

</div>

Thanks Emmanuel, I did not correctly interpret the meaning of the Err\_To\_Out parameter.

I want to keep the full output/input/error stream after run, as files.

I see that there is Get\_Input\_Fd function returning File\_Descriptor, but I don’t see how to manipulate files that way : most operation in OS\_Lib expect a file name as input, and not a Fd.

Do you remember how are intended to be used those Get\_xxx\_Fd functions in Expect ?

---

<div class="post-metadata">

### Author: ![ebriot](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/ebriot/32/156_2.png) [@ebriot](https://forum.ada-lang.io/u/ebriot)
#### Post date: [November 14, 2024, 5:19pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/12 "2024-11-14T17:19:17Z")

</div>

I think I would use `GNAT.OS_Lib.Read` (though I think it might be blocking, so maybe I would also call `fcntl()` to make it non-blocking, at least on linux). Your external process might itself end up blocking if you do not read fast enough from your end of the pipe. Have a look at the Get\_Command\_Output function.

The interface is certainly not the most efficient, because it was difficult to make it compatible for both linux and windows, if I remember right. But it seems like this is a core part of your tool, so likely worth spending some time on it,.

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [November 14, 2024, 5:55pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/13 "2024-11-14T17:55:41Z")

</div>

> [@LionelDraghi](#):
>
> I want to keep the full output/input/error stream after run, as files

The variant Text\_Bufferred\_Process from [GTKAda contributions](http://www.dmitry-kazakov.de/ada/gtkada_contributions.htm#10.2) captures output and error pipes into text buffers. The input pipe is fed from a text buffer too.

If you want for some reason to store into files, then use Asynchronous\_Process from [GTKAda contributions](http://www.dmitry-kazakov.de/ada/gtkada_contributions.htm#10.1) offers a variant with primitive operations **Input** , **Output** , **Error**. Override **Input** to read a file, **Output** and **Error** to write files.

---

<div class="post-metadata">

### Author: ![bjorn-lundin](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/bjorn-lundin/32/106_2.png) [@bjorn-lundin](https://forum.ada-lang.io/u/bjorn-lundin)
#### Post date: [November 19, 2024, 8:14pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/14 "2024-11-19T20:14:08Z")

</div>

perhaps look at [ada-util](https://github.com/stcarrez/ada-util?tab=readme-ov-file)  
and its pipes. see example at [process creation](https://blog.vacs.fr/vacs/blogs/post.html?post=2012/03/16/Process-creation-in-Java-and-Ada)

---

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [November 19, 2024, 11:25pm UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/15 "2024-11-19T23:25:39Z")

</div>

Thanks for the pointer, [ada-util/src/sys/processes/util-processes.ads at 0d2660318d3862cd36d9e1f8708c3d8e32541204 · stcarrez/ada-util · GitHub](https://github.com/stcarrez/ada-util/blob/0d2660318d3862cd36d9e1f8708c3d8e32541204/src/sys/processes/util-processes.ads) (and the Tools child package) seems well suited for my needs.

---

<div class="post-metadata">

### Author: ![LionelDraghi](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/lioneldraghi/32/416_2.png) [@LionelDraghi](https://forum.ada-lang.io/u/LionelDraghi)
#### Post date: [October 10, 2026, 7:20am UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/16 "2026-10-10T07:20:48Z")

</div>

One good side of AI agents: you can now afford to experiment deeply with every candidate, in separate branches, before arbitrating. I had GLM 5.3 study the alternatives discussed in this thread.

What made the difference: ada-util is portable to the three platforms, and its pseudo terminal comes already configured in raw mode — no echo, no canonical mode, exact bytes — which is what bbt’s interactive steps need, and what Spawn could not be configured to provide; and its raw streams expose the underlying descriptors, so the missing event model is workable. GNAT.Expect was eliminated early: its consuming, sliding buffer discards older data, which doesn’t fit bbt’s exact output checks.

bbt 0.5.0 runs with Spawn and works very well, but the next version moves to ada-util: no library-defect workarounds are needed anymore, and no fixed pumping latency either — Spawn costs a fixed ~40 ms per command, and the features suite drops from about 27 s to 9 s. The price: a much larger crate, and no pty on Windows, where the pump needed a per-platform port (`PeekNamedPipe`) — CI is now green on the three platforms. The measured comparison (complexity, execution time, robustness, dependencies, Windows paths) is detailed in [bbt’s design discussions](https://github.com/LionelDraghi/bbt/blob/dfa3ab6ef4e761926e8987c94934397eb359343b/docs/dev/design_discussions.md) for those who want more.

Both projects received upstream contributions: [spawn#37](https://github.com/AdaCore/spawn/issues/37) and [#38](https://github.com/AdaCore/spawn/issues/38) (Windows monitor bugs the old engine worked around), and [ada-util#72](https://github.com/stcarrez/ada-util/issues/72)–[#74](https://github.com/stcarrez/ada-util/issues/74) (`Wait_Event`, `Set_Terminal_Size`, a bounded `Wait` — the last two already as PRs [#75](https://github.com/stcarrez/ada-util/pull/75) and [#76](https://github.com/stcarrez/ada-util/pull/76)).

This is not something I could have done alone. One of the good sides of AI is that chasing the consequences of a problem beyond one’s own project becomes very effective: I believe one of the consequences of AI usage will be, contrary to what everyone dreads, a general gain in the quality of open source software (even if here I don’t have the maintainers’ feedback yet, and my pull requests may well be bad!!).

---

<div class="post-metadata">

### Author: ![charlie5](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/charlie5/32/626_2.png) [@charlie5](https://forum.ada-lang.io/u/charlie5)
#### Post date: [October 10, 2026, 9:53am UTC](https://forum.ada-lang.io/t/spawn-lib-choice/1467/17 "2026-10-10T09:53:50Z")

</div>

I worked on a project which addressed these requirements some years ago, in collaboration with Jacob ‘Sparre’ Anderson. The project was named [aShell](https://github.com/charlie5/aShell) and allowed spawning OS processes and interacting with them. The project supported both ‘safe’ spawns (where multiple spawns could be made from separate tasks) and ‘unsafe’ spawns (which restricted spawns to a single task). The need for the safe/unsafe versions was due to tasking and spawning subtleties, which is described in the GNAT documentation.

Sadly, Jacob passed away several months after the project commenced.

The project was mostly completed except for several aspects of the ‘safe’ version. The greatest shortcoming of the project was reliance on the Florist Ada posix binding, which only supports Linux, FreeBSD and Darwin (macOS). The lack of Windows support is very unfortunate and probably a blocker for many potential users. I believe there is a wPosix binding for Windows but I don’t know if it could be used. The API is very different to that of Florist.
