Fil-C on Nix

Filnix wiki

mbrock/filnix

ports/qt5.nix

Qt 5.15 for Fil-C, as an override of the qt5 scope (libsForQt5 is built from final.qt5, so its packages see these modules too). How Fil-C builds see the build platform's Qt is in build-tools.nix. Check with

nix build --impure --expr 'import ./tests/qt5.nix { }'

which runs widgets, networking, SQL, XML, SVG, QML/JavaScript, Qt Quick and Quick Controls 2 programs on the offscreen platform and under Xvfb.

λpkgs, final
λqfinal qprev
let
10inherit pkgs
lib

qtModule puts the scope's qmake hook in nativeBuildInputs, where splicing picks the build platform's: that hook propagates the native qtbase, and the module's setup hooks then fail with "detected mismatched Qt dependencies". Use this scope's own hook, which adds the Fil-C qtbase (whose qmake and moc are build-platform programs).

16qmakeHook=
qfinal.qmake.__spliced.hostTarget or qfinal.qmake
used by withFilcQmake
17withFilcQmake=
drv: drv.overrideAttrsold: nativeBuildInputs = mapd: if d.name or "" ≡ "qmake-hook" then qmakeHook else d old.nativeBuildInputs or [ ];
uses qmakeHook

Qt modules' build tools (qtdeclarative's qmlcachegen and qmltyperegistrar, native programs in its dev output) are looked up on PATH, where a cross build has only the build platform's Qt.

27withQtToolsOnPath=
λdrv
drv.overrideAttrs
λold
30preConfigure=
for p in "''${pkgsHostTarget[@]}"; do
if [ -d "$p/mkspecs" ] && [ -d "$p/bin" ]; then addToSearchPath PATH "$p/bin"; fi
done
+
old.preConfigure or ""
37modules=
"qt3d" "qtcharts" "qtconnectivity" "qtdatavis3d" "qtgamepad" "qtgraphicaleffects" "qtimageformats" "qtlocation" "qtlottie" "qtmultimedia" "qtnetworkauth" "qtpositioning" "qtpurchasing" "qtquick3d" "qtquickcontrols" "qtquickcontrols2" "qtremoteobjects" "qtscript" "qtscxml" "qtsensors" "qtserialbus" "qtserialport" "qtspeech" "qtsvg" "qtsystems" "qtvirtualkeyboard" "qtwayland" "qtwebchannel" "qtwebglplugin" "qtwebsockets" "qtx11extras" "qtxmlpatterns"
in
//

Needs no other module's tools (and qtdeclarative, a long build, depends on it).

//

qtbase's setup hook collects mkspecs for QMAKEPATH only from nativeBuildInputs, which in a cross build are the build platform's; a module's Qt dependencies (qtdeclarative for qtquickcontrols2, say) are buildInputs, so qmake did not see them ("Some of the required modules (qtHaveModule(quick)) are not available. Skipped."). Collect them too.

84qmake=
qprev.qmake.overrideAttrs
λold
85buildCommand=
old.buildCommand
+
cat >> $out/nix-support/setup-hook <<'EOF'
filcQmakeHostPathHook() {
if declare -F qmakePathHook >/dev/null; then qmakePathHook "$1"; fi
}
addEnvHooks "$targetOffset" filcQmakeHostPathHook
EOF
95qtbase=
qprev.qtbase.overridewithGtk3 = false; libxkbcommon = final.libxkbcommon.overrideAttrsold: mesonFlags = mapf: if f ≡ "-Denable-x11=false" then "-Denable-x11=true" else f old.mesonFlags; postPatch = old.postPatch or "" + '' substituteInPlace meson.build --replace-fail \ "if get_option('enable-x11') has_xvfb" \ "if false has_xvfb" '';;.overrideAttrs

The GTK 3 port has no X11 backend, and Qt's GTK 3 platform theme (native file dialogs and styling) includes <gdk/gdkx.h>.

The xcb platform plugin needs libxkbcommon-x11, which the shared libxkbcommon port leaves out (-Denable-x11=false). Its X11 tests (which need Xvfb) find their cases through __start_/__stop_ section symbols, which do not link under Fil-C, so leave those out.

λold
118patches=
old.patches
++

Qt refuses pkg-config in a cross build without a sysroot, and a sysroot of / turns -L/nix/store/... into -L//nix/store/..., which the compiler wrapper then drops as an impure path.

Every file that includes <QtCore> outside QtCore emits a .qtversion section as module-level assembly, which crashes the Fil-C compiler. The header is installed, so this covers users too.

QtCore's CPU feature probe saves %rbx around CPUID and spells XGETBV as bytes; Fil-C only lowers the canonical forms. RDRAND has no lowering, so leave it to the kernel's generator. Its Valgrind check is a client request (inline assembly), which stops too.

QProcess's forkfd probes waitid(P_PIDFD) and clone(CLONE_PIDFD) through syscall(), which Fil-C refuses by stopping the program.

QMutexLocker, QReadLocker and QWriteLocker keep their lock's address in a quintptr with a "locked" bit, QMap nodes their parent with the colour bit, and QModelIndex its internal pointer. Integer fields lose the capability, so these are pointers now.

QByteArray/QString::fromRawData point their header at the data with an offset from the header, so data() had the header's bounds; raw-data headers keep the data pointer instead.

Fil-C ignores section attributes on data, so plugins have no .qtmetadata section; find the metadata in the whole file.

QThread names threads with prctl(PR_SET_NAME, (unsigned long)name); the integer has no capability, so the runtime's prctl trapped.

QSemaphore wakes with FUTEX_WAKE_OP, which Fil-C's futex refuses.

Nixpkgs configures a cross qtbase with the linux-generic-g++ device spec and CROSS_COMPILE=${targetPrefix}. Fil-C's compiler has no target prefix, and the spec refuses an empty CROSS_COMPILE. Point it at the wrapper's bin directory instead; the wrapper has ar, nm, objcopy and strip there, and cc and c++ rather than gcc and g++. The compiler is Clang, which prints no LIBRARY_PATH for qmake's default-library-path probe; as clang, qmake asks -print-search-dirs.

161configureFlags=
mapf: if f ≡ "CROSS_COMPILE=" then "CROSS_COMPILE=${final.stdenv.cc}/bin/" else f old.configureFlags
165preConfigure=
old.preConfigure
+
cat >> mkspecs/devices/linux-generic-g++/qmake.conf <<'EOF'
QMAKE_COMPILER = gcc clang llvm
QMAKE_CC = $${CROSS_COMPILE}cc
QMAKE_CXX = $${CROSS_COMPILE}c++
QMAKE_LINK = $${QMAKE_CXX}
QMAKE_LINK_SHLIB = $${QMAKE_CXX}
QMAKE_LINK_C = $${QMAKE_CC}
QMAKE_LINK_C_SHLIB = $${QMAKE_CC}
EOF
178qtlocation=
withQtToolsOnPathwithFilcQmakeqprev.qtlocation.overrideAttrs
λold

The bundled Mapbox GL uses Boost 1.65, which needs std::unary_function; libc++ removed it in C++17 unless asked to keep it (Nixpkgs builds Qt with GCC and libstdc++).

184env=
old.env or { }
//
185NIX_CFLAGS_COMPILE=
toString
old.env.NIX_CFLAGS_COMPILE or ""
"-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"

Clang, unlike GCC, rejects its narrowing conversions.

"-Wno-c++11-narrowing"
"-Wno-c++11-narrowing-const-reference"
195qtmultimedia=
withQtToolsOnPathwithFilcQmakeqprev.qtmultimedia.overrideAttrs
λold

GType is a pointer in Fil-C's GLib, and C++ cannot switch on one.

199patches=
202qtquickcontrols2=
withQtToolsOnPathwithFilcQmakeqprev.qtquickcontrols2.overrideAttrs
λold

QQuickDeferredPointer keeps an item pointer and two flag bits in a quintptr; the first ComboBox trapped in setIndicator().

QtScript is the 2009 JavaScriptCore: its JIT emits machine code and the module-level assembly of JITStubs.cpp crashes the compiler; its values are NaN-boxed integers and its collector scans the native stack conservatively, which cannot find Fil-C's pointers. Deprecated in Qt 5.5; QJSEngine (qtdeclarative) works.

217qtscript=
qprev.qtscript.overrideAttrsold: meta = old.meta // broken = true;;
223qtdeclarative=
withFilcQmakeqprev.qtdeclarative.overrideAttrs
λold

The V4 engine NaN-boxes heap pointers into quint64 values and passes them around as integers (ReturnedValue), which drops their capabilities; keep them pointer-typed. Also QJSValue, PropertyKey, QFlagPointer/QBiPointer, QQmlNotifierEndpoint and sparse-array nodes, which keep pointers with flag bits in integers.

The V4 JIT writes machine code at run time; use the interpreter.

231qmakeFlags=
old.qmakeFlags or [ ] ++ "--" "-no-feature-qml-jit"
237qttools=
λold

libclang and libllvm are only for qdoc, which Qt then leaves out; they would be a Fil-C build of LLVM.

242buildInputs=
[ ]
243patches=
builtins.filterp: ¬pkgs.lib.hasInfix"libclang-main-header" toStringp old.patches