#!/bin/zsh # Build script for dune on Hammerhead # # illumos patches (readdir d_type, winsize, wait4) applied via [patches] set -e cd "$SRCDIR" # Ensure OCaml is in PATH export PATH="$PREFIX/bin:$PATH" # Force 64-bit compilation export CC="gcc -m64" export CXX="g++ -m64" export CFLAGS="-m64" export CXXFLAGS="-m64" export LDFLAGS="-m64 $LDFLAGS" # libatomic is at /usr/lib (hammerhead BSD layout) export LD_LIBRARY_PATH="/usr/lib:${LD_LIBRARY_PATH:-}" export LIBRARY_PATH="/usr/lib:${LIBRARY_PATH:-}" # Bootstrap dune (creates _boot/dune.exe) ocaml boot/bootstrap.ml # Build dune for release (packager mode) gmake release # Install dune to package directory gmake install PREFIX=$PREFIX DESTDIR="$PKGDIR" --- otherlibs/stdune/src/readdir.c 2026-03-11 20:49:26.711769000 -0500 +++ otherlibs/stdune/src/readdir.c 2026-03-11 20:49:26.713044510 -0500 @@ -1,3 +1,16 @@ +/* + * readdir.c - illumos/Solaris compatible version for dune + * + * This file replaces otherlibs/stdune/dune_filesystem_stubs/readdir.c + * in dune to fix compilation on illumos/Solaris where struct dirent + * does not have d_type field. + * + * Apply by copying this file to: + * dune-X.Y.Z/otherlibs/stdune/dune_filesystem_stubs/readdir.c + * + * For opam's bundled dune-local: + * opam-X.Y.Z/src_ext/dune-local/otherlibs/stdune/dune_filesystem_stubs/readdir.c + */ #include #include @@ -13,12 +26,27 @@ #include #include #include + +/* illumos/Solaris compatibility - d_type not in struct dirent */ +#ifndef DT_UNKNOWN +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 +#define ILLUMOS_NO_DTYPE 1 +#endif + typedef struct dirent directory_entry; value val_file_type(int typ) { switch(typ) { -#ifndef __HAIKU__ +#ifndef __HAIKU__ case DT_REG: return Val_int(0); case DT_DIR: @@ -64,10 +92,11 @@ v_filename = caml_copy_string(e->d_name); v_tuple = caml_alloc_small(2, 0); Field(v_tuple, 0) = v_filename; -#ifndef __HAIKU__ - Field(v_tuple, 1) = val_file_type(e->d_type); -#else +#if defined(__HAIKU__) || defined(ILLUMOS_NO_DTYPE) + /* illumos/Solaris don't have d_type, always return unknown */ Field(v_tuple, 1) = Val_int(7); +#else + Field(v_tuple, 1) = val_file_type(e->d_type); #endif CAMLreturn(v_tuple); } --- vendor/notty/src-unix/native/winsize.c 2026-03-11 20:49:26.714264146 -0500 +++ vendor/notty/src-unix/native/winsize.c 2026-03-11 20:49:26.715364983 -0500 @@ -1,44 +1,55 @@ +/* + * winsize.c - illumos/Solaris compatible version for notty (used by dune) + * + * This file replaces vendor/notty/src-unix/native/winsize.c + * in dune to fix compilation on illumos/Solaris where TIOCGWINSZ + * is defined in sys/termios.h rather than sys/ioctl.h. + * + * Apply by copying this file to: + * dune-X.Y.Z/vendor/notty/src-unix/native/winsize.c + * + * For opam's bundled dune-local: + * opam-X.Y.Z/src_ext/dune-local/vendor/notty/src-unix/native/winsize.c + */ + #include +#include +#include -#ifdef _WIN32 -#include +#ifdef __sun +/* illumos/Solaris: struct winsize and TIOCGWINSZ in termios */ +#include +#include #else #include -#include #endif -#ifdef __HAIKU__ -/* On some platforms, ioctl() is declared in . */ #include -#endif - -CAMLprim value caml_notty_winsize (value vfd) { -#ifdef _WIN32 - (void) vfd; - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - if (hConsole == INVALID_HANDLE_VALUE) return Val_int (0); +#include - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo(hConsole, &csbi)) { - int columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; - int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; - return Val_int ((columns << 16) + ((rows & 0x7fff) << 1)); - } - return Val_int (0); -#else - int fd = Int_val (vfd); +CAMLprim value caml_notty_winsize (value fd) { +#ifdef TIOCGWINSZ struct winsize w; - if (ioctl (fd, TIOCGWINSZ, &w) >= 0) - return Val_int ((w.ws_col << 16) + ((w.ws_row & 0x7fff) << 1)); - return Val_int (0); + if (ioctl(Int_val(fd), TIOCGWINSZ, &w) == -1) + caml_failwith("ioctl"); + value res = caml_alloc_small(2, 0); + Field(res, 0) = Val_int(w.ws_col); + Field(res, 1) = Val_int(w.ws_row); + return res; +#else + /* Fallback: return default 80x24 */ + value res = caml_alloc_small(2, 0); + Field(res, 0) = Val_int(80); + Field(res, 1) = Val_int(24); + return res; #endif } -CAMLprim value caml_notty_winch_number (value vunit) { - (void) vunit; -#ifdef _WIN32 - return Val_int (0); +/* Return SIGWINCH signal number */ +CAMLprim value caml_notty_winch_number (value unit) { +#ifdef SIGWINCH + return Val_int(SIGWINCH); #else - return Val_int (SIGWINCH); + return Val_int(0); #endif } --- otherlibs/stdune/src/wait4_stubs.c 2026-03-11 20:49:26.716620588 -0500 +++ otherlibs/stdune/src/wait4_stubs.c 2026-03-11 20:49:26.717848047 -0500 @@ -1,83 +1,51 @@ #include - #ifdef _WIN32 #include - -void dune_wait4(value v_pid, value flags) { - (void)flags; - caml_failwith("wait4: not supported on windows"); -} - +void dune_wait4(value v_pid, value flags) { caml_failwith("wait4: not supported"); } #else - #include #include #include #include - #include #include #include #include - +#include +#include #define TAG_WEXITED 0 #define TAG_WSIGNALED 1 #define TAG_WSTOPPED 2 - -CAMLextern int caml_convert_signal_number(int); CAMLextern int caml_rev_convert_signal_number(int); - static value alloc_process_status(int status) { value st; - - if (WIFEXITED(status)) { - st = caml_alloc_small(1, TAG_WEXITED); - Field(st, 0) = Val_int(WEXITSTATUS(status)); - } else if (WIFSTOPPED(status)) { - st = caml_alloc_small(1, TAG_WSTOPPED); - Field(st, 0) = Val_int(caml_rev_convert_signal_number(WSTOPSIG(status))); - } else { - st = caml_alloc_small(1, TAG_WSIGNALED); - Field(st, 0) = Val_int(caml_rev_convert_signal_number(WTERMSIG(status))); - } + if (WIFEXITED(status)) { st = caml_alloc_small(1, TAG_WEXITED); Field(st, 0) = Val_int(WEXITSTATUS(status)); } + else if (WIFSTOPPED(status)) { st = caml_alloc_small(1, TAG_WSTOPPED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WSTOPSIG(status))); } + else { st = caml_alloc_small(1, TAG_WSIGNALED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WTERMSIG(status))); } return st; } - static int wait_flag_table[] = {WNOHANG, WUNTRACED}; - -// see https://man7.org/linux/man-pages/man2/waitpid.2.html for a description of -// possible v_pid values -1 is the one we typically use, which means wait for -// any child process value dune_wait4(value v_pid, value flags) { CAMLparam2(v_pid, flags); CAMLlocal2(times, res); - - int status, cv_flags; - struct timeval tp; - cv_flags = caml_convert_flag_list(flags, wait_flag_table); - pid_t pid = Int_val(v_pid); - + int pid, status, cv_flags = caml_convert_flag_list(flags, wait_flag_table); struct rusage ru; - + struct timeval tp; + memset(&ru, 0, sizeof(ru)); caml_enter_blocking_section(); - // returns the pid of the terminated process, or -1 on error - pid = wait4(pid, &status, cv_flags, &ru); + pid = waitpid(Int_val(v_pid), &status, cv_flags); + if (pid > 0) getrusage(RUSAGE_CHILDREN, &ru); gettimeofday(&tp, NULL); caml_leave_blocking_section(); - if (pid == -1) - uerror("wait4", Nothing); - + if (pid == -1) uerror("wait4", Nothing); times = caml_alloc_small(2 * Double_wosize, Double_array_tag); Store_double_field(times, 0, ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6); Store_double_field(times, 1, ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1e6); - res = caml_alloc_tuple(4); Store_field(res, 0, Val_int(pid)); Store_field(res, 1, alloc_process_status(status)); - Store_field(res, 2, - caml_copy_double(((double)tp.tv_sec + (double)tp.tv_usec / 1e6))); + Store_field(res, 2, caml_copy_double(((double)tp.tv_sec + (double)tp.tv_usec / 1e6))); Store_field(res, 3, times); CAMLreturn(res); } - #endif # dune - OCaml build system # https://dune.build/ [package] name = "dune" version = "3.20.2" release = 1 description = "Fast, portable, and opinionated build system for OCaml" url = "https://dune.build/" license = "MIT" maintainer = "Chris Tusa " arch = "x86_64" [dependencies] runtime = ["ocaml"] build = ["ocaml", "make"] [[source]] file = "dune-3.20.2.tar.gz" urls = ["https://github.com/ocaml/dune/archive/refs/tags/3.20.2.tar.gz"] checksum = "579c738f8ca191ba0a9b22dbe78f1377542442c9827cf4939f8964f09f9edb28" [patches] files = ["dune-illumos-compat.patch"] strip = 0 [build] parallel = true /* * readdir.c - illumos/Solaris compatible version for dune * * This file replaces otherlibs/stdune/dune_filesystem_stubs/readdir.c * in dune to fix compilation on illumos/Solaris where struct dirent * does not have d_type field. * * Apply by copying this file to: * dune-X.Y.Z/otherlibs/stdune/dune_filesystem_stubs/readdir.c * * For opam's bundled dune-local: * opam-X.Y.Z/src_ext/dune-local/otherlibs/stdune/dune_filesystem_stubs/readdir.c */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #include /* illumos/Solaris compatibility - d_type not in struct dirent */ #ifndef DT_UNKNOWN #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 #define DT_WHT 14 #define ILLUMOS_NO_DTYPE 1 #endif typedef struct dirent directory_entry; value val_file_type(int typ) { switch(typ) { #ifndef __HAIKU__ case DT_REG: return Val_int(0); case DT_DIR: return Val_int(1); case DT_CHR: return Val_int(2); case DT_BLK: return Val_int(3); case DT_LNK: return Val_int(4); case DT_FIFO: return Val_int(5); case DT_SOCK: return Val_int(6); case DT_UNKNOWN: return Val_int(7); #endif default: return Val_int(7); } } CAMLprim value caml__dune_filesystem_stubs__readdir(value vd) { CAMLparam1(vd); CAMLlocal2(v_filename, v_tuple); DIR * d; directory_entry * e; d = DIR_Val(vd); if (d == (DIR *) NULL) unix_error(EBADF, "readdir", Nothing); caml_enter_blocking_section(); errno = 0; e = readdir((DIR *) d); caml_leave_blocking_section(); if (e == (directory_entry *) NULL) { if(errno == 0) { CAMLreturn(Val_int(0)); } else { uerror("readdir", Nothing); } } v_filename = caml_copy_string(e->d_name); v_tuple = caml_alloc_small(2, 0); Field(v_tuple, 0) = v_filename; #if defined(__HAIKU__) || defined(ILLUMOS_NO_DTYPE) /* illumos/Solaris don't have d_type, always return unknown */ Field(v_tuple, 1) = Val_int(7); #else Field(v_tuple, 1) = val_file_type(e->d_type); #endif CAMLreturn(v_tuple); } #else CAMLprim value caml__dune_filesystem_stubs__readdir(value vd) { unix_error(ENOSYS, "readdir", Nothing); } #endif #include #ifdef _WIN32 #include void dune_wait4(value v_pid, value flags) { caml_failwith("wait4: not supported"); } #else #include #include #include #include #include #include #include #include #include #include #define TAG_WEXITED 0 #define TAG_WSIGNALED 1 #define TAG_WSTOPPED 2 CAMLextern int caml_rev_convert_signal_number(int); static value alloc_process_status(int status) { value st; if (WIFEXITED(status)) { st = caml_alloc_small(1, TAG_WEXITED); Field(st, 0) = Val_int(WEXITSTATUS(status)); } else if (WIFSTOPPED(status)) { st = caml_alloc_small(1, TAG_WSTOPPED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WSTOPSIG(status))); } else { st = caml_alloc_small(1, TAG_WSIGNALED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WTERMSIG(status))); } return st; } static int wait_flag_table[] = {WNOHANG, WUNTRACED}; value dune_wait4(value v_pid, value flags) { CAMLparam2(v_pid, flags); CAMLlocal2(times, res); int pid, status, cv_flags = caml_convert_flag_list(flags, wait_flag_table); struct rusage ru; struct timeval tp; memset(&ru, 0, sizeof(ru)); caml_enter_blocking_section(); pid = waitpid(Int_val(v_pid), &status, cv_flags); if (pid > 0) getrusage(RUSAGE_CHILDREN, &ru); gettimeofday(&tp, NULL); caml_leave_blocking_section(); if (pid == -1) uerror("wait4", Nothing); times = caml_alloc_small(2 * Double_wosize, Double_array_tag); Store_double_field(times, 0, ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6); Store_double_field(times, 1, ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1e6); res = caml_alloc_tuple(4); Store_field(res, 0, Val_int(pid)); Store_field(res, 1, alloc_process_status(status)); Store_field(res, 2, caml_copy_double(((double)tp.tv_sec + (double)tp.tv_usec / 1e6))); Store_field(res, 3, times); CAMLreturn(res); } #endif /* * winsize.c - illumos/Solaris compatible version for notty (used by dune) * * This file replaces vendor/notty/src-unix/native/winsize.c * in dune to fix compilation on illumos/Solaris where TIOCGWINSZ * is defined in sys/termios.h rather than sys/ioctl.h. * * Apply by copying this file to: * dune-X.Y.Z/vendor/notty/src-unix/native/winsize.c * * For opam's bundled dune-local: * opam-X.Y.Z/src_ext/dune-local/vendor/notty/src-unix/native/winsize.c */ #include #include #include #ifdef __sun /* illumos/Solaris: struct winsize and TIOCGWINSZ in termios */ #include #include #else #include #endif #include #include CAMLprim value caml_notty_winsize (value fd) { #ifdef TIOCGWINSZ struct winsize w; if (ioctl(Int_val(fd), TIOCGWINSZ, &w) == -1) caml_failwith("ioctl"); value res = caml_alloc_small(2, 0); Field(res, 0) = Val_int(w.ws_col); Field(res, 1) = Val_int(w.ws_row); return res; #else /* Fallback: return default 80x24 */ value res = caml_alloc_small(2, 0); Field(res, 0) = Val_int(80); Field(res, 1) = Val_int(24); return res; #endif } /* Return SIGWINCH signal number */ CAMLprim value caml_notty_winch_number (value unit) { #ifdef SIGWINCH return Val_int(SIGWINCH); #else return Val_int(0); #endif }