I wanted a serial port on macOS, so I did the obvious thing and reached for GNAT.Serial_Communications. The manual
does say GNU/Linux and Windows only. I tried anyway.
The simple case worked. Open the calculator first, then the application, and the link came up. Establishing it the other
way round did not. Timeouts did not fire, so a retry never ran. I spent the afternoon tuning those timeouts so the two
ends could meet in either order, staring at Searching for Calculator . and waiting for the second dot — the one
that means a retry — which never appeared.
That is the undesired behaviour. Not a dead port: a port that works until the program depends on a timeout. Linux and
macOS both have termios, tcflag_t and speed_t, and from the Ada side those names look portable. They are not the
same bits.
On Linux the baud rate is a token in c_cflag, in the CBAUD / CBAUDEX field. B9600 is 8#000015# — thirteen, not
9600 — and B115200 is 8#0010002#. tcflag_t and speed_t are unsigned int. cfsetospeed writes the token; the
kernel turns the token into a line speed. Classic struct termios has no separate speed members.
On macOS both types are unsigned long. The structure has c_ispeed and c_ospeed, and cfsetospeed stores a numeric
rate in c_ospeed. There B9600 really is 9600. A rate that is not one of the B* constants goes through the
IOSSIOSPEED ioctl, not through another flag token.
So a Linux body pointed at a Darwin port is not a clean failure. A plain transfer can still complete, which is why
opening the calculator first looked fine. What does not survive is the rest of the structure. tcflag_t is a different
width, speed_t is a numeric rate on macOS and a CBAUD token on Linux, and the timeout lives in c_cc as VMIN /
VTIME — the array that, on macOS, is followed by c_ispeed and c_ospeed. Feed it the Linux layout and a read that
should have timed out waits instead. The second dot never comes.
The fix is in the adacl_serial crate, version 8.0.0.
AdaCL.Serial_Communications keeps the GNAT specification. On Linux and Windows it is a rename of
GNAT.Serial_Communications, because that body is the right one there. On macOS it is a Darwin body: numeric speed_t,
the macOS layout, and IOSSIOSPEED where the rate is not a standard constant. AdaCL.Serial_IO sits on top — a null
extension, so the calls read as Port.Put_Line and Port.Expect — and is proven to SPARK gold. Application code uses
the one package name on every host.
I have written up how to open a port, including why Block => False matters for a calculator protocol, at
https://adacl.sourceforge.net/adacl-serial/. GNATdoc is the reference for the subprograms; that page is only the
start.
The proper long-term home for the Darwin body is GCC. I have not prepared that patch — the contribution process is new
to me. If someone who already knows how a GCC change is put together is willing to help, I would like to upstream it.
Shameless vanity: having my name in the GCC source tree would be cool.
Thanks in advance.