Has anyone tried to compile Crystal with cosmopolitan libc for a portable binary?
If not, how hard is it generally to make Crystal work with an alternative libc?
Has anyone tried to compile Crystal with cosmopolitan libc for a portable binary?
If not, how hard is it generally to make Crystal work with an alternative libc?
I’m not aware of anyone trying that.
Effort depends on how much the interface differs from the existing C standard libraries. But it’s likely not that much for the majority of bindings. So you could copy and paste a lot. I’d expect to get probably > 90% working pretty quickly. Figuring out all the details in edge cases can be time consuming, though.
Previous mention on the issue tracker: Can crystal lang implement the APE binary format? · Issue #12753 · crystal-lang/crystal · GitHub
I might give this a go after the Battlesnake spring league (a few months). My free time is pretty booked until then.
I just tried the first step: compile a hello world with an empty prelude (i.e. no stdlib). There are zero reasons that it wouldn’t work.
class String
def to_unsafe : UInt8*
pointerof(@c)
end
end
lib LibC
fun puts(UInt8*) : Int32
end
LibC.puts("hello world")
Thanks to --cross-compile
and --target
we can build a couple object files for the x86_64 and aarch64 architectures. I used x86_64-linux-gnu
and aarch64-linux-gnu
as pseudo targets.
I ran cosmocc -v hello.c
to figure out the underlying calls to gcc
and apelink
, and repeated these on the previously built object file, and I got a hello.com
file
The next steps for adding support would be:
figure out limitations:
add a couple targets for Cosmopolitan in Crystal:
x86_64-unknown-cosmo
aarch64-unknown-cosmo
write C bindings in src/lib_c/<target>
;
src/crystal/system
); most of unix
should work… but they might need to be specific to cosmocc?link executable (for the current target)
codegen both object files (for both targets) in parallel + linker calls, at least for release builds;
…
Then are the external C libraries. I assume most should work as long as they’re compiled with cosmocc
(x86_64 and aarch64). They should compile and behave as if they’d been compiled for POSIX
I’m more reserved on Boehm GC, however… or not.
We can bind external runtime symbols, for example for errno values, but we can’t declare enum Errno
that requires the value to be known at compilation time.
For example this compiles:
lib LibC
fun printf(UInt8*, ...) : Int32
$enosys = ENOSYS : Int32
end
LibC.printf("ENOSYS=%d\n", LibC.enosys)
But this won’t compile:
enum Errno
ENOSYS = LibC.enosys
# ^---
# Error: invalid constant value
end
Maybe we can fake it? Yeah, we can probably fake it with a struct
wrapping an Int32
and acting as an enum
, then Errno::ENOSYS = new(LibC.enosys)
.
Also we can’t call LibC::ENOSYS
anymore (not really an issue).