Ren-C Builds on Haiku!

I didn't realize bringing this up to date would take as long to tinker around with as it did (~ 8 hours), but...


Why was it such a pain?...

GCC 2.95.3 was Released in March 2001

Don't let the 2017 build date fool you. That's a 20-year old compiler, which we assume has the occasional Haiku patch here and there.

I knew the compiler that came with Haiku had been old. But when I was making decisions about letting go of C89 support (in the core) I figured that Haiku would be bumping their versions to support C99 at some point...

Nope.

The reason Haiku is still so far back appears to be because they depend on GCC 2.95 features that enable them to get binary compatibility with old BeOS packages.

You can install a more modern GCC if you like--but then you're off in the concerns of having multiple toolchains on your system. And presumably it's harder to link up against the HaikuOS C++ platform APIs. So I thought it would be a good proof of our light dependencies to build on what's there.

Note: Not Just Us, mbedTLS Dropped Their C89 Support

Even the most die -hard of low-dependency systems have let go of the 80s. In 2019, mbedTLS ceased supporting C89:

"We've internally decided to drop support for C89 in the development branch. We've removed -Wdeclaration-after-statement to our build options in CMakeLists.txt. We run some builds with gcc -std=c99 -pedantic so we do test C99 compliance to a reasonable extent. Thus this issue is resolved."

Options Were Either Give Up, Or Revive the C++98 Build

At one point Ren-C was able to build as C++98. I stopped seeing the point of maintaining this option...as it was silly to be keeping track of which features I could use from C++11 in which permutations. So it was either you build as C or you build as C++11 (or higher).

But there really is no other choice with this GCC 2.95. The code is now styled:

printf("Here's a statement\n");

const char *str = "Here's a declaration after a statement";

The Haiku C compilation mode requires all your definitions to be before the statements:

const char *str; 

printf("Here's a statement\n");

str = "Here's a declaration after a statement";

(Note: I hate that, because it makes maintenance much harder. But libuv is actually a holdout here...they're carrying the torch for C89 and committers still have to code in that style.)

In any case, C++98 was able to do that. So I revived the setting. It wasn't too hard...I just made it act like the C build almost all of the time. Hence no use of inheritance in REBVAL or anything like that. It deviates only when it has to for compiler semantics.

Building mbedTLS Was Saved By A --pedantic Quirk

While Ren-C was designed to compile as C or C++, mbedTLS is plain C. C++ compilers will choke on some of the permissive treatment of void*.

I thought there'd be no way around this; because C++ compilers have no way to disable these errors. ...or so I thought...!

As it turns out, if you raise the error reporting level with "pedantic" warnings, the warning machinery notices the problems before the error! And you can ignore warnings!

So oddly enough, I actually could compile the encryption code on Haiku (without having to go in and edit it).

Small Rewrites Needed To Dodge Compiler Bugs

Minor changes were required to code that was triggering GCC 2.95 bugs that prevented the build. I wouldn't be happy if I had to make a dozen changes for it, but it was a few.

I guess even if you write code that is correct, if you notice it triggering a compiler bug that may be a sign that it involves something hard for compilers to get right. One might be able to balance the annoyance of having to make the change with the potential that maybe fewer compilers would be getting that bit wrong.

Networking Doesn't Work, But After LibUV Switch, It Might!

I built the entirety of LibUV into the executable (even though I didn't technically have to). And the filesystem code--which is now on libuv--seems to be working.

The networking isn't on libuv yet, and isn't seeming to work. But maybe once it switches over, it will. I mentioned that Haiku was one of the explicitly supported platforms of libuv. So if all goes smoothly in that conversion, I imagine this will work!

No Action Item Here, Just Proving It Could Be Done

This was a good head-check of what exactly has changed as Ren-C has refactored and evolved. Letting certain dependencies creep in can make your source less adaptable and agile...and this helps be assured that things are still on track.

What's important here isn't "oh, now we can have millions of HaikuOS users". Rather it serves as another example of managed dependency control--to lend confidence to the idea that porting to other new ones should be easy enough.

6 Likes

It's fascinating to know about Haiku, and that Ren-C is able to port there.
I'm new to Ren-C and I just built Ren-C on my MacBook. It was a smooth experience. I tried a few Rebol-derived binaries, and only Ren-C works out of the box on both of my MacBooks.

To be pedantic, definitions and declarations are different, which you probably know.
Anyway, I like putting declarations before statements because I try to write C as portable assembly, keeping in mind how the assembly would look like when writing C. Putting declarations first makes it easier to reason about the assembly.

2 Likes

Hello there... and welcome... though we continue to be in an extremely experimental state which is really trying to explore what kinds of potentials there are in the odd medium of Rebol-like languages. It's tough to onboard anyone as a user or participant, and so generally things just stay quiet, imagining that it would be the sort of thing that gets advertised "...someday".

(There are some cool tricks, but also a huge mess of half-implemented and not-documented things. I think of it more like an interesting game than I do a tool for "real work". All of its quirky novelty isn't going to help anyone with a deadline. However, there's certainly interesting things to see for those who are persistent with it.)

Glad to hear it. "rebmake" is kind of a disaster, and this post explains how it got that way:

New Build Executables, New Build Strategy

As crazy as maintaining a bootstrap for that is, it does help exercise more code. The goal of Ren-C is to be a maximally skinnable and bendable language, so bending to act like older iterations of itself is one of those challenges.

I like putting declarations before statements because I try to write C as portable assembly, keeping in mind how the assembly would look like when writing C.

Philosophy of initializing variables at their point of declaration aside...you might be a good candidate for appreciating what Ren-C is attempting to accomplish.

But please introduce yourself a bit in the Introductions Section and I can try to answer or address anything particular that has made you curious about the project.

3 Likes

So Haiku does now provide a modern toolchain out of the box.

All you have to do is say setarch x86 and suddenly you've got gcc 13.3

Despite being modern, it's missing some things on Haiku like Address Sanitizer. But it works well enough.

After a long night of tinkering and consulting with AI... I have my working branch running, as the first R3-derived executable with networking on Haiku! (thanks to using libuv...)

What Work Had To Be Done?

We're still in a state where the bootstrap executable is a different wonky old codebase that's fast and stable enough to build the current very-experimental branch.

So this means I had to get two codebases working, with fairly different concerns.

It did take a pretty long time, but a lot of that was me figuring out what to use for a debugger on Haiku, and just generally struggling with the medium. (Finding the setarch x86 was itself a good hour of discovery.) The ultimate changes were somewhat minimal:

Haiku Memory Alignment is Non-Standard

Ren-C has to have careful consideration to make sure that when raw memory is returned from a malloc() to be laid out by the system, that manual layout puts 64-bit double at 64-bit aligned pointers.

The way we do this alignment is by trusting that what comes back from malloc() is itself 64-bit aligned. This is required by the standard:

"If allocation succeeds, returns a pointer that is suitably aligned for any object type with fundamental alignment."

That should include double, hence at least 64-bits. But Haiku's legacy aligns on 32-bit, and we have asserts and problems that start firing if we notice bad alignment.

I found a switch for -malign-double which tells gcc to align on 64-bit boundaries and I thought that fixed it well enough. But unfortunately any Haiku system calls you make expect the structures to not have that alignment, and so you start to notice problems once you're making OS calls.

There's things you can do, like ask for an allocation that's slightly larger, and track when you have to offset it to match the alignment. But the reason Haiku has managed to get away with this bad behavior is because they only have distributions on x86 (and now x64), where bad double alignments still work... they're just slower. There's no need to bulletproof Ren-C for a non-C-standards-compliant system... in the 32-bit version of the OS... just to make accesses to double precision floating point faster.

So the right answer here was just to turn off alignment checking for Haiku (discovered only after figuring out -malign-double was the culprit for breaking OS calls).

Haiku's int_fast8_t can hold 255

This is something where I screwed up, and said int_fast8_t where I should have said uint_fast8_t. As it so happens, Mac ARM and x86 Linux and Windows all use a signed char to implement int_fast8_t, so if you assign 255 to it you'll get -1 ... but Haiku uses a larger signed int, so it's still 255.

Pretty random, but took a while to figure out what was going on.

Explicit includes needed some places

Haiku apparently factors the headers a bit more. e.g. you can't just say #include <termios.h> and get everything, you have to say:

#include <sys/select.h>  // for FD_ZERO, FD_SET, select()
#include <sys/time.h>    // for struct timeval
#include <sys/types.h>   // for fd_set

#include <termios.h>

Different-sized network address info for IPv4 addresses

As a kind of sanity check on what was going on, I had an assert that for an IPv4 address:

assert(req.addrinfo->ai_addrlen == 16);

But for whatever reason this isn't true on Haiku, it has seemingly extra space...that you can ignore. It's voodoo to me. I changed the assert:

assert(req.addrinfo->ai_addrlen >= 16);

Anyway, Gave Up on the C++98 Build

It was an interesting trick to use the super old compiler to build Ren-C. But it's no longer necessary, and I think TCC represents a better choice for a minimal target--more likely to be applicable to strange embedded platforms than Haiku's old compiler.

2 Likes