Fil-C on Nix

Filnix wiki

mbrock/filnix

rubyports.nix

See docs/ruby-filc-porting-patterns.txt for common porting patterns.

λpkgs, final?
pkgs
finaldefault pkgs
let

final has Fil-C packages, pkgs is base nixpkgs

8inherit import./ports inherit pkgs lib pkgs;
for use addCFlag

Force building native extension (needed for cross-compilation)

15native=
usedontBuild = false;
uses use
17replace=
λfile old new
λattrs
20postPatch=
attrs.postPatch or ""
+
substituteInPlace ${file} \
--replace-fail ${pkgs.lib.escapeShellArgold} \
${pkgs.lib.escapeShellArgnew}
uses use

AST-based pattern replacement using ast-grep
Use --selector call_expression for C function calls

29astGrep=
λpattern rewrite
λattrs
32nativeBuildInputs=
attrs.nativeBuildInputs or [ ] ++ pkgs.ast-grep
33postPatch=
attrs.postPatch or ""
+
ast-grep run -p ${pkgs.lib.escapeShellArgpattern} \
-r ${pkgs.lib.escapeShellArgrewrite} \
-l c --selector call_expression -U .
uses use

AST-grep without selector (for switch/case patterns)

41astGrepAny=
λpattern rewrite
λattrs
44nativeBuildInputs=
attrs.nativeBuildInputs or [ ] ++ pkgs.ast-grep
45postPatch=
attrs.postPatch or ""
+
ast-grep run -p ${pkgs.lib.escapeShellArgpattern} \
-r ${pkgs.lib.escapeShellArgrewrite} \
-l c -U .
uses use

AST-grep with custom selector (e.g., switch_statement)

53astGrepSel=
λselector pattern rewrite
λattrs
56nativeBuildInputs=
attrs.nativeBuildInputs or [ ] ++ pkgs.ast-grep
57postPatch=
attrs.postPatch or ""
+
ast-grep run -p ${pkgs.lib.escapeShellArgpattern} \
-r ${pkgs.lib.escapeShellArgrewrite} \
-l c --selector ${selector} -U .
uses use
in
66ruby-terminfo
native
replace"terminfo.c" "RTEST(ret)" "RTEST((VALUE)ret)"
71msgpack
native
replace"ext/msgpack/buffer_class.c" "unsigned long max = ((VALUE*) args)[2];" "unsigned long max = (unsigned long)(uintptr_t)((VALUE*) args)[2];"
replace"ext/msgpack/buffer_class.c" "(VALUE) max," "(VALUE)(uintptr_t) max,"

ffi - Uses Fil-C's libffi (bundled libffi has incompatible asm)

  • USE_FFI_ALLOC disables custom x86_64 trampolines (incompatible with Fil-C)
  • VALUE-as-pointer issues fixed
  • libffi comes from final.defaultGemConfig (Fil-C version)
86ffi
native
addCFlag"-DUSE_FFI_ALLOC"

Patch extconf.rb to use system libffi
Skip cross-compile link test but explicitly add -lffi to linker flags

replace"ext/ffi_c/extconf.rb" "libffi_ok &&= have_library(\"ffi\"" "$libs << \" -lffi\"; libffi_ok &&= true || have_library(\"ffi\""

Fix VALUE-as-pointer bitwise op in Struct.h

replace"ext/ffi_c/Struct.h" "#define FIELD_CACHE_LOOKUP(this, sym) ( &(this)->cache_row[((sym) >> 8) & 0xff] )" "#define FIELD_CACHE_LOOKUP(this, sym) ( &(this)->cache_row[(((uintptr_t)(sym)) >> 8) & 0xff] )"

openssl - various VALUE/int mismatches (fixed via ast-grep patterns)

102openssl
native

rb_attr takes int, not VALUE - fix Qfalse -> 0

astGrep"rb_attr($A, $B, $C, $D, Qfalse)" "rb_attr($A, $B, $C, $D, 0)"

rb_cstr_to_inum takes int badcheck, not VALUE

astGrep"rb_cstr_to_inum($A, $B, Qtrue)" "rb_cstr_to_inum($A, $B, 1)"

rb_str_new size cast needs uintptr_t

astGrep"rb_str_new(NULL, (long)$A)" "rb_str_new(NULL, (long)(uintptr_t)$A)"

rb_protect expects VALUE, but len is long

replace"ext/openssl/ossl.c" "str = rb_protect(ossl_str_new_i, len, &state);" "str = rb_protect(ossl_str_new_i, (VALUE)(uintptr_t)len, &state);"

int ret = Qnil -> int ret = -1 (function returns int, not VALUE)

replace"ext/openssl/ossl_pkcs7.c" "int i, ret = Qnil;" "int i, ret = -1;"

VALUE ret stores ID values, should be ID ret

replace"ext/openssl/ossl_pkey_ec.c" "point_conversion_form_t form;\n VALUE ret;" "point_conversion_form_t form;\n ID ret;"

racc - ERROR_TOKEN (int) passed where VALUE expected, use vERROR_TOKEN

124racc
native
replace"ext/racc/cparse/cparse.c" "SHIFT(v, act, ERROR_TOKEN, val)" "SHIFT(v, act, vERROR_TOKEN, val)"

eventmachine - Intern_* vars are ID, not VALUE; also int reuse in set_rlimit

132eventmachine
native
replace"ext/rubymain.cpp" "static VALUE Intern_" "static ID Intern_"
replace"ext/rubymain.cpp" "arg = (NIL_P(arg)) ? -1 : NUM2INT (arg);" "int limit = (NIL_P(arg)) ? -1 : NUM2INT(arg);"
replace"ext/rubymain.cpp" "return INT2NUM (evma_set_rlimit_nofile (arg));" "return INT2NUM(evma_set_rlimit_nofile(limit));"

date - VALUE to st_index_t conversion for hash function

144date
native
astGrepSel"assignment_expression" "h[0] = m_nth($X)" "h[0] = (st_index_t)(uintptr_t)m_nth($X)"
astGrepSel"assignment_expression" "h[3] = m_sf($X)" "h[3] = (st_index_t)(uintptr_t)m_sf($X)"

nokogiri - can't switch on VALUE (pointer), convert to if-else

155nokogiri
native
astGrepSel"switch_statement" "switch (rb_range_beg_len($ARG, $BEG, $LEN, $SIZE, $FLAG)) { case Qfalse: break; case Qnil: return Qnil; default: return subseq($SELF, $B, $L); }" "{ VALUE range_result = rb_range_beg_len($ARG, $BEG, $LEN, $SIZE, $FLAG); if (range_result == Qfalse) { } else if (range_result == Qnil) { return Qnil; } else { return subseq($SELF, $B, $L); } }"

nio4r - patch bundled libev inline asm, fix VALUE/int issues Note: bundled libev has GVL-release patches needed for Ruby threading

165nio4r
native

Replace inline asm mfence macro with __atomic_thread_fence in bundled libev ast-grep matches #define as preproc_def with body as preproc_arg (raw text) Must match exact whitespace in the #define line

astGrepAny"#define ECB_MEMORY_FENCE __asm__ __volatile__ (\"mfence\" : : : \"memory\")" "#define ECB_MEMORY_FENCE __atomic_thread_fence(__ATOMIC_SEQ_CST)"

Don't reuse VALUE param for int bitwise op results

replace"ext/nio4r/monitor.c" "interest = monitor->interests | NIO_Monitor_symbol2interest(interest);" "int new_interest = monitor->interests | NIO_Monitor_symbol2interest(interest);"
replace"ext/nio4r/monitor.c" "interest = monitor->interests & ~NIO_Monitor_symbol2interest(interest);" "int new_interest = monitor->interests & ~NIO_Monitor_symbol2interest(interest);"
replace"ext/nio4r/monitor.c" "NIO_Monitor_update_interests(self, (int)interest);" "NIO_Monitor_update_interests(self, new_interest);"

curb - idCall/idJoin are ID not VALUE; int return from VALUE function

187curb
native
replace"ext/curb_easy.c" "static VALUE idCall;" "static ID idCall;"
replace"ext/curb_easy.c" "static VALUE idJoin;" "static ID idJoin;"
replace"ext/curb_multi.c" "static VALUE idCall;" "static ID idCall;"
replace"ext/curb_postfield.c" "static VALUE idCall;" "static ID idCall;"
replace"ext/curb_multi.c" "return method == Qtrue ? 1 : 0;" "return (method == Qtrue) ? Qtrue : Qfalse;"

bigdecimal - switch on VALUE, rb_protect int<->VALUE casting

199bigdecimal
native

Convert switch(val) case Qnil/Qtrue/Qfalse to if-else

replace"ext/bigdecimal/bigdecimal.c" "switch (val) { case Qnil: case Qtrue: case Qfalse:" "if (val == Qnil || val == Qtrue || val == Qfalse) {"
replace"ext/bigdecimal/bigdecimal.c" "return Qnil; default: break; }" "return Qnil; }"

is_zero()/is_one() return int, not VALUE - return 0 instead of Qfalse

astGrepSel"case_statement" "case T_BIGNUM: return Qfalse;" "case T_BIGNUM: return 0;"

Fix rb_protect callback: store result in struct instead of casting int<->VALUE

replace"ext/bigdecimal/bigdecimal.c" "const char *exp_chr; size_t ne; };" "const char *exp_chr; size_t ne; int result; };"
replace"ext/bigdecimal/bigdecimal.c" "return (VALUE)VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne);" "x->result = VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne); return Qnil;"
replace"ext/bigdecimal/bigdecimal.c" "VALUE result = rb_protect(call_VpCtoV, (VALUE)&args, &state);" "rb_protect(call_VpCtoV, (VALUE)&args, &state);"
replace"ext/bigdecimal/bigdecimal.c" "return (int)result;" "return args.result;"

io-console - switch on VALUE needs uintptr_t cast

246io-console
native

Cast switch expressions to uintptr_t

astGrepAny"switch ($X)" "switch ((uintptr_t)$X)"

Cast case labels to uintptr_t

astGrepAny"case Qtrue:" "case (uintptr_t)Qtrue:"
astGrepAny"case Qfalse:" "case (uintptr_t)Qfalse:"
astGrepAny"case Qundef:" "case (uintptr_t)Qundef:"
astGrepAny"case Qnil:" "case (uintptr_t)Qnil:"

Database drivers - just need native flag, nixpkgs provides buildInputs

258sqlite3
native