Skip to content

feat: linux compatibility#20

Open
pikdum wants to merge 2 commits intoNexus-Mods:masterfrom
pikdum:feat/linux
Open

feat: linux compatibility#20
pikdum wants to merge 2 commits intoNexus-Mods:masterfrom
pikdum:feat/linux

Conversation

@pikdum
Copy link

@pikdum pikdum commented Feb 18, 2026

  • tweaks to build and run natively on linux
  • some small linter/warning fixes
  • flake.nix for dev shell

Don't have Windows to verify that I didn't break that build, though.

I used this flake.nix to build libloot.so.0 v0.27.0 from https://github.com/loot/libloot:

{
  description = "Nix flake for building libloot (shared library)";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      lib = nixpkgs.lib;
      forAllSystems = lib.genAttrs [ "x86_64-linux" "aarch64-linux" ];
    in {
      packages = forAllSystems (system:
        let
          pkgs = import nixpkgs { inherit system; };

          espluginTarball = pkgs.fetchurl {
            url = "https://github.com/Ortham/esplugin/archive/refs/tags/6.1.3.tar.gz";
            hash = "sha256-0RNzuRCANujYRT/3cACuKWH50SQ8mcQt1zLa0Mg9hhg=";
          };

          libloadorderTarball = pkgs.fetchurl {
            url = "https://github.com/Ortham/libloadorder/archive/refs/tags/18.4.0.tar.gz";
            hash = "sha256-yD8fSIKeAT8mNAQbYv7YxJV/t0aaQBJQs3k5U+08PSw=";
          };

          lootConditionInterpreterTarball = pkgs.fetchurl {
            url = "https://github.com/loot/loot-condition-interpreter/archive/refs/tags/5.3.2.tar.gz";
            hash = "sha256-EaLGKJj2RQza5ttgfGP9eJj2K/Z9SHS3zu09NpMzGvM=";
          };

          yamlCppTarball = pkgs.fetchurl {
            url = "https://github.com/loot/yaml-cpp/archive/0.8.0+merge-key-support.3.tar.gz";
            hash = "sha256-4gZ/Grj2WK64qHlf0OBqdTtBULk1LK4EcoDmWQeN1E8=";
          };

          mkSource = name: tarball:
            pkgs.runCommand "${name}-source" { } ''
              mkdir -p "$out"
              tar -xzf ${tarball} --strip-components=1 -C "$out"
            '';

          mkFfiRustLibrary = {
            pname,
            version,
            src,
            cargoLockFile,
            manifestPath,
            staticLibName,
            cbindgenDir,
            cbindgenOutput,
            cbindgenArgs ? "",
          }:
            pkgs.rustPlatform.buildRustPackage {
              inherit pname version src;
              cargoLock.lockFile = cargoLockFile;
              cargoBuildFlags = [ "--manifest-path" manifestPath ];
              nativeBuildInputs = [ pkgs.rust-cbindgen ];
              doCheck = false;

              installPhase = ''
                runHook preInstall

                mkdir -p "$out/lib" "$out/include"
                staticLibPath=$(find target -type f -name "${staticLibName}" | head -n1)
                cp "$staticLibPath" "$out/lib/"
                cbindgen ${cbindgenArgs} ${cbindgenDir}/ -o "$out/include/${cbindgenOutput}"

                runHook postInstall
              '';
            };

          espluginSource = mkSource "esplugin-6.1.3" espluginTarball;
          libloadorderSource = mkSource "libloadorder-18.4.0" libloadorderTarball;
          lootConditionInterpreterSource = mkSource "loot-condition-interpreter-5.3.2" lootConditionInterpreterTarball;
          yamlCppSource = mkSource "yaml-cpp-0.8.0-merge-key-support.3" yamlCppTarball;

          espluginFfi = mkFfiRustLibrary {
            pname = "esplugin-ffi";
            version = "6.1.3";
            src = espluginSource;
            cargoLockFile = "${espluginSource}/Cargo.lock";
            manifestPath = "ffi/Cargo.toml";
            staticLibName = "libesplugin_ffi.a";
            cbindgenDir = "ffi";
            cbindgenOutput = "esplugin.hpp";
          };

          libloadorderFfi = mkFfiRustLibrary {
            pname = "libloadorder-ffi";
            version = "18.4.0";
            src = libloadorderSource;
            cargoLockFile = "${libloadorderSource}/Cargo.lock";
            manifestPath = "ffi/Cargo.toml";
            staticLibName = "libloadorder_ffi.a";
            cbindgenDir = "ffi";
            cbindgenOutput = "libloadorder.hpp";
            cbindgenArgs = "-l c++";
          };

          lootConditionInterpreterFfi = mkFfiRustLibrary {
            pname = "loot-condition-interpreter-ffi";
            version = "5.3.2";
            src = lootConditionInterpreterSource;
            cargoLockFile = "${lootConditionInterpreterSource}/Cargo.lock";
            manifestPath = "ffi/Cargo.toml";
            staticLibName = "libloot_condition_interpreter_ffi.a";
            cbindgenDir = "ffi";
            cbindgenOutput = "loot_condition_interpreter.h";
          };

          liblootPkg = pkgs.stdenv.mkDerivation {
            pname = "libloot";
            version = "0.27.0";

            src = ./.;
            strictDeps = true;
            preferLocalBuild = true;

            nativeBuildInputs = with pkgs; [
              cmake
              ninja
              pkg-config
              git
              python3
            ];

            buildInputs = with pkgs; [
              boost
              icu
              tbb
              fmt
              spdlog
            ];

            postPatch = ''
              python - <<'PY'
              from pathlib import Path

              path = Path("CMakeLists.txt")
              text = path.read_text()

              replacements = [
                  (
                      "if(NOT DEFINED ESPLUGIN_URL)",
                      "if(NOT (DEFINED ESPLUGIN_INCLUDE_DIRS AND DEFINED ESPLUGIN_LIBRARIES))\nif(NOT DEFINED ESPLUGIN_URL)",
                  ),
                  (
                      "if(NOT DEFINED LIBLOADORDER_URL)",
                      "endif()\n\nif(NOT (DEFINED LIBLOADORDER_INCLUDE_DIRS AND DEFINED LIBLOADORDER_LIBRARIES))\nif(NOT DEFINED LIBLOADORDER_URL)",
                  ),
                  (
                      "if(NOT DEFINED LOOT_CONDITION_INTERPRETER_URL)",
                      "endif()\n\nif(NOT (DEFINED LCI_INCLUDE_DIRS AND DEFINED LCI_LIBRARIES))\nif(NOT DEFINED LOOT_CONDITION_INTERPRETER_URL)",
                  ),
                  (
                      "FetchContent_Declare(\n    fmt",
                      "endif()\n\nFetchContent_Declare(\n    fmt",
                  ),
                  (
                      "add_dependencies(loot\n    esplugin\n    libloadorder\n    loot-condition-interpreter)",
                      "if(TARGET esplugin)\n    add_dependencies(loot esplugin)\nendif()\nif(TARGET libloadorder)\n    add_dependencies(loot libloadorder)\nendif()\nif(TARGET loot-condition-interpreter)\n    add_dependencies(loot loot-condition-interpreter)\nendif()",
                  ),
              ]

              for old, new in replacements:
                  if old not in text:
                      raise SystemExit(f"Patch pattern not found: {old!r}")
                  text = text.replace(old, new, 1)

              path.write_text(text)
              PY
            '';

            cmakeFlags = [
              "-DCMAKE_BUILD_TYPE=Release"
              "-DBUILD_SHARED_LIBS=ON"
              "-DLIBLOOT_BUILD_TESTS=OFF"
              "-DLIBLOOT_INSTALL_DOCS=OFF"

              "-DESPLUGIN_INCLUDE_DIRS=${espluginFfi}/include"
              "-DESPLUGIN_LIBRARIES=${espluginFfi}/lib/libesplugin_ffi.a;dl"

              "-DLIBLOADORDER_INCLUDE_DIRS=${libloadorderFfi}/include"
              "-DLIBLOADORDER_LIBRARIES=${libloadorderFfi}/lib/libloadorder_ffi.a;dl"

              "-DLCI_INCLUDE_DIRS=${lootConditionInterpreterFfi}/include"
              "-DLCI_LIBRARIES=${lootConditionInterpreterFfi}/lib/libloot_condition_interpreter_ffi.a;dl"

              "-DFETCHCONTENT_SOURCE_DIR_YAML-CPP=${yamlCppSource}"
            ];

            meta = with pkgs.lib; {
              description = "libloot shared library";
              homepage = "https://github.com/loot/libloot";
              license = licenses.gpl3Only;
              platforms = platforms.linux;
            };
          };
        in {
          libloot = liblootPkg;
          default = liblootPkg;
        });

      devShells = forAllSystems (system:
        let
          pkgs = import nixpkgs { inherit system; };
        in {
          default = pkgs.mkShell {
            packages = with pkgs; [
              cmake
              ninja
              pkg-config
              git
              rustc
              cargo
              rust-cbindgen
              boost
              icu
              tbb
              fmt
              spdlog
            ];
          };
        });
    };
}

That bit definitely seems over-complicated, probably simpler to use docker or their github actions. :)

@pikdum pikdum marked this pull request as ready for review February 18, 2026 04:25
@IDCs
Copy link
Contributor

IDCs commented Mar 11, 2026

@Sewer56 - conflicts aside which are easy to fix, can you please test this on your end as I don't have a linux environment?

Can confirm this works just fine on Windows

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants