Fil-C on Nix

Filnix wiki

mbrock/filnix

ports/default.nix

Fil-C Ports DSL and Overlay Infrastructure

This file provides:
1. The DSL for defining ports (pin, patch, configure, etc.)
2. The overlay-making machinery that converts port lists to overlays

λlib, pkgs
let
13inherit lib
foldl'

The state we transform through the pipeline

16Init=
17overrideArgs=
{ }
Args for .override
18attrs=
_: { }
Attrs for .overrideAttrs
used by acc

Compose two attribute transformers

22merge=
λf g old
let
25step1=
used by step2
26step2=
uses step1
in
used by overAttrs

State transformers

31overArgs=
a: st: st // overrideArgs = st.overrideArgs // a;
used by arg
32overAttrs=
f: st: st // attrs = mergest.attrs f;
uses merge used by use

Pin a specific version and hash
Automatically substitutes version in the existing URL
Usage: pin "2.1.12" "sha256-..."

45pin=
λversion hash
λold
let
50pname=
old.pname or builtins.parseDrvNameold.name.name
used by originalUrl
51oldVersion=
"${old.version or ""}"
used by newUrl

Get the original URL and replace old version with new version This handles most common URL patterns automatically

55originalUrl=
old.src ? urls
→
builtins.headold.src.urls
old.src ? url
→
old.src.url
else
throw"Cannot extract URL from ${pname} source"
uses pname used by newUrl

Replace old version with new version in URL
Try multiple patterns: x.y.z, x.y, x_y_z, etc.

in
68inherit
version pname
69name=
"${pname}-${version}"
70src=
pkgs.fetchurl
72inherit
hash
uses use

Full source override (for when pin doesn't work)
Usage: src "2.1.12" "sha256-..." (v: "https://.../${v}.tar.gz")

79src=
λversion hash urlFn
λold
let
84pname=
old.pname or builtins.parseDrvNameold.name.name
in
87inherit
version pname
88name=
"${pname}-${version}"
89src=
pkgs.fetchurl
91inherit
hash
uses use

Extract pname from package

97extractPname=
λpkg
pkg ? pname
→
pkg.pname
pkg ? name
→
builtins.parseDrvNamepkg.name.name
else
throw"Cannot extract pname from package"
used by pname

Build a port with automatic pname extraction
Usage: for pkgs.coreutils [skipCheck]

for "flask" [skipCheck]  (for Python/sub-overlays)

For explicit names: { bash = for pkgs.bash [...]; }

110for=
λpkg steps
let
113isCustomDrv=
builtins.isPathpkg ∨ builtins.isStringpkg ∧ lib.hasSuffix".nix" pkg
used by pname spec
115isStringName=
builtins.isStringpkg ∧ ¬lib.hasSuffix".nix" pkg
used by pname
123acc=
uses Init foldl' used by spec
124spec=
→
127__customDrv=
128__attrs=
acc.attrs
else
inherit acc attrs overrideArgs;
in
spec // inherit pname;

Common helpers reason parameter is for documentation only - doesn't affect the build

137skipTests=
λreason
140doCheck=
false
141doInstallCheck=
false
uses use
144skipCheck=
reason: usedoCheck = false;
uses use
149parallelize=
useenableParallelBuilding = true;
uses use
150serialize=
useenableParallelBuilding = false;
uses use
152tool=
x: useold: nativeBuildInputs = old.nativeBuildInputs or [ ] ++ x;
uses use
157link=
x: useold: buildInputs = old.buildInputs or [ ] ++ x;
uses use
163patch=
p: useold: patches = old.patches or [ ] ++ p;
uses use
168skipPatch=
p: useold: patches = builtins.filterpatch: ¬lib.hasInfixp toStringpatch old.patches or [ ];
uses use
176removeCFlag=
λflag
λold
179env=
old.env or { } // NIX_CFLAGS_COMPILE = lib.replaceStringsflag "" old.env or { }.NIX_CFLAGS_COMPILE or "";
184preConfigure=
old.preConfigure or ""
+
export NIX_CFLAGS_COMPILE="''${NIX_CFLAGS_COMPILE//${flag}/}"
uses use
189addCFlag=
flag: useold: env = old.env or { } // NIX_CFLAGS_COMPILE = old.env or { }.NIX_CFLAGS_COMPILE or "" + " ${flag}";;
uses use
197addCMakeFlag=
flag: useold: cmakeFlags = old.cmakeFlags or [ ] ++ flag;
uses use
202removeCMakeFlag=
flag: useold: cmakeFlags = builtins.filterf: f ≢ flag old.cmakeFlags or [ ];
uses use
208configure=
flag: useold: configureFlags = old.configureFlags or [ ] ++ flag;
uses use
213removeConfigureFlag=
flag: useold: configureFlags = builtins.filterf: f ≢ flag old.configureFlags or [ ];
uses use
219addMakeFlag=
flag: useold: makeFlags = old.makeFlags or [ ] ++ flag;
uses use
224removeMakeFlag=
flag: useold: makeFlags = builtins.filterf: f ≢ flag old.makeFlags or [ ];
uses use
230addMesonFlag=
flag: useold: mesonFlags = old.mesonFlags or [ ] ++ flag;
uses use
235removeMesonFlag=
flag: useold: mesonFlags = builtins.filterf: ¬lib.hasPrefixflag f old.mesonFlags or [ ];
uses use

Mark as broken for Fil-C with a reason
Only sets broken/badPlatforms when actually cross-compiling to Fil-C

245broken=
λreason
λold
248meta=
old.meta or { }
//
249broken=
true
250filcBrokenReason=
251badPlatforms=
"x86_64-linux" "aarch64-linux"
uses use

Mark packages as memory-safe when built with Fil-C
Removes knownVulnerabilities since memory safety eliminates whole classes of bugs

260markAsNotNecessarilyInsecure=
λold
261meta=
old.meta or { }
//
262knownVulnerabilities=
[ ]
263description=
old.meta.description or "Package" + " (Memory-safe via Fil-C)"
uses use
268wip=
throw"Work in progress"

AST-based code rewriting using ast-grep
Usage: astRewrite "file.c" "c" "pattern with $VAR" "replacement with $VAR" Patterns use ast-grep syntax: $VAR for single node, $$$VAR for multiple nodes

273astRewrite=
λfile lang pattern replacement
λold
276nativeBuildInputs=
old.nativeBuildInputs or [ ] ++ pkgs.ast-grep
277postPatch=
old.postPatch or ""
+
ast-grep --pattern ${lib.escapeShellArgpattern} \
--rewrite ${lib.escapeShellArgreplacement} \
--lang ${lang} \
--update-all \
${file}
uses use

URL helpers

287github=
λorgRepo pathFn v
let
290parts=
lib.splitString"/" orgRepo
used by org repo
291org=
builtins.elemAtparts 0
uses parts
292repo=
builtins.elemAtparts 1
uses parts
in
"https://github.com/${org}/${repo}/releases/download/${pathFnv}"
296gnu=
pkg: v: "mirror://gnu/${pkg}/${pkg}-${v}.tar.xz"
297gnuTarGz=
pkg: v: "mirror://gnu/${pkg}/${pkg}-${v}.tar.gz"

DSL functions to export

300dsl=
301inherit
for
302inherit
arg use pin src broken markAsNotNecessarilyInsecure
310inherit
skipTests skipCheck parallelize serialize tool link patch skipPatch removeCFlag addCFlag removeCMakeFlag addCMakeFlag addMakeFlag removeMakeFlag addMesonFlag removeMesonFlag configure removeConfigureFlag wip astRewrite
332inherit
github gnu gnuTarGz

Create an overlay from a list of port specs
Each item in portList is either:

- A port spec: { pname, attrs, overrideArgs, ... }
- An attrset with explicit names: { foo = <port-spec>; bar = <port-spec>; }
339makeOverlay=
λportList final prev
let

Convert list to flat attrset keyed by pname
Handles both direct specs and attrset-wrapped specs

344portSpecs=
builtins.listToAttrs
lib.flatten
map
λitem
item ? pname
→

Direct port spec with pname field

351name=
item.pname
352value=

Attrset with explicit names: { foo = <spec>; bar = <spec>; } Extract each key-value pair

else
lib.mapAttrsToList
λname spec
358inherit
name
359value=
used by ported

Apply each spec to the corresponding package in prev

366ported=
prev.lib.mapAttrs
λname spec
spec ? __customDrv
→
final.callPackagespec.__customDrv { }.overrideAttrsspec.__attrs

Custom derivation - call it with final context for auto-injected deps

prev ? ${name}
→
let

Package exists in nixpkgs - apply overlay transformations

374base=
spec.overrideArgs ≢ { }
→
prev.${name}.overridespec.overrideArgs
else
in
base.overrideAttrsspec.attrs

Package not in nixpkgs and not a custom drv - shouldn't happen

else
throw"Package '${name}' not found in nixpkgs and no __customDrv provided"
uses portSpecs
in
//
rec

Emacs package sets using our ported emacs30

390emacsPackages=
prev.emacsPackagesForported.emacs30
391emacs30Packages=
prev.emacsPackagesForported.emacs30

Python aliases - python312 is explicitly named, provides both python3 and python312

394python3=
ported.python312
395python312Packages=
ported.python312.pkgs
396python3Packages=
lib.dontRecurseIntoAttrspython312Packages

Perl package set using our ported perl

399perl=
ported.perl5
400perl5Packages=
ported.perl5.pkgs
401perlPackages=
lib.dontRecurseIntoAttrsperl5Packages

Nixpkgs' default moved to Ruby 3.4 (YJIT needs a Rust target); keep the ported 3.3, as the default was before.

405ruby=
ported.ruby_3_3
406rubyPackages=
final.rubyPackages_3_3

Nixpkgs only defines glibc locales for isGnu hosts, so for Fil-C glibcLocales was null and packages that export LC_ALL=en_US.UTF-8 got no LOCALE_ARCHIVE (typeguard's sphinx-build died in setlocale). The archive is data: the native build works for both the build tools and Fil-C's glibc, which reads it via LOCALE_ARCHIVE too.

413glibcLocales=
final.buildPackages.glibcLocales
414glibcLocalesUtf8=
final.buildPackages.glibcLocalesUtf8

pkg-config alias

417pkg-config=
prev.pkg-config.override
418pkg-config=
final.pkgconf-unwrapped
419baseBinName=
"pkgconf"

Re-call defaultGemConfig with final packages so gems get Fil-C dependencies This ensures ffi gem gets final.libffi (Fil-C version), not pkgs.libffi

424defaultGemConfig=
final.callPackageprev.path + "/pkgs/development/ruby-modules/gem-config"

Darwin-only args that don't exist in final (not ported to Fil-C)

428inherit prev.darwin
DarwinTools
429inherit prev
autoSignDarwinBinariesHook
in
dsl // inherit makeOverlay;