From 5f6065690c5e3cc3ca3279d2f3122b065e1f69ac Mon Sep 17 00:00:00 2001 From: MadMcCrow Date: Sun, 16 Apr 2023 19:48:06 +0200 Subject: [PATCH] Upgrade godot-flake : - splits flake function - adds GDExtension support - exposes Functions to build Extension in other flakes. - updates patches to godot - updates Godot to 4.1-dev --- .gitignore | 5 ++ custom.nix | 59 ++++++++++++++++++++ extensions.nix | 93 ++++++++++++++++++++++++++++++ flake.lock | 34 ++++++++--- flake.nix | 121 ++++++++++++++++++++++------------------ godot.nix | 101 +++++++++++++++++++++++++++++++++ libs.nix | 61 ++++++++++++++++++++ patches/gl.patch | 13 +++++ patches/godot-cpp.patch | 13 +++++ patches/xfixes.patch | 15 +++++ version.nix | 8 +++ 11 files changed, 459 insertions(+), 64 deletions(-) create mode 100644 .gitignore create mode 100644 custom.nix create mode 100644 extensions.nix create mode 100644 godot.nix create mode 100644 libs.nix create mode 100644 patches/gl.patch create mode 100644 patches/godot-cpp.patch create mode 100644 patches/xfixes.patch create mode 100644 version.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecb3fac --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +#!/usr/bin/git + +## NIX +# result from `nix build` +result diff --git a/custom.nix b/custom.nix new file mode 100644 index 0000000..18c1800 --- /dev/null +++ b/custom.nix @@ -0,0 +1,59 @@ +# custom.nix +# this modules focuses on generating or overriding the custom.py used to build godot +{ lib, system, optimize ? "speed", mono ? true, llvm ? true, lto ? true +, opengl ? true, udev ? true, fontconfig ? true, touch ? false, speechd ? false +, dbus ? true, pulseaudio ? true, }: +let + + linux = (system == "x86_64-linux"); + + # TODO support override : + override = false; + file = "./custom.py"; + + # convert parameters to set + options = lib.mkIf (override == false) { + # optimize is one of "size"or "speed" + optimize = optimize; + # C# support + module_mono_enabled = mono; + # llvm + use_llvm = llvm; + use_lld = llvm; + # link time optim + use_lto = lto; + # add a suffix to binaries + extra_suffix = "_flake"; + # enable openGL3ES renderer + opengl3 = opengl; + # linux pulseaudio + pulseaudio = pulseaudio && linux; + # Use D-Bus to handle screensaver and portal desktop settings + dbus = dbus && linux; + # Use Speech Dispatcher for Text-to-Speech support + speechd = speechd && linux; + # Use fontconfig for system fonts support + fontconfig = fontconfig && linux; + # Use udev for gamepad connection callbacks + udev = udev && linux; + # Enable touch events + touch = touch; + }; + + # convert true/false to "yes" "no" for scons + boolToString = cond: if cond then "yes" else "no"; + + # turn option set into scons options + mkGodotOption = optionSet: + (lib.mapAttrsToList (k: v: + if (builtins.isBool v) then + ("${k}=${boolToString v}") + else + "${k}=${builtins.toJSON v}") optionSet); + +in { + + # we can use (lib.mapAttrsToList (k: v: "${k}=${builtins.toJSON v}") options); if we have values in nix format + # resulting scons flag + customSconsFlags = (mkGodotOption options); +} diff --git a/extensions.nix b/extensions.nix new file mode 100644 index 0000000..39aecd7 --- /dev/null +++ b/extensions.nix @@ -0,0 +1,93 @@ +# extension.nix +# this modules focuses on building cool extensions for godot +{ lib, pkgs, system, inputs }: +with pkgs; +with builtins; +let + # godot version infos + godotVersion = import ./version.nix { inherit system; }; + # godot custom.py + godotCustom = import ./custom.nix { inherit lib system; }; + # godot build libraries + godotLibraries = import ./libs.nix { inherit pkgs; }; + + # dependancies + nativeBuildInputs = godotLibraries.buildTools ++ godotLibraries.buildDep; + buildInputs = godotLibraries.runtimeDep; + runtimeDependencies = godotLibraries.runtimeDep; + +in rec{ + # + # Godot-cpp bindings : they are required to + # valid values for target are: ('editor', 'template_release', 'template_debug' + # + mkGodotCPP = args @ {target, ...} : stdenv.mkDerivation ({ + # make name: + name = (concatStringsSep "-" ["godot-cpp" target godotVersion.version]); + version = godotVersion.version; + src = inputs.godot-cpp; + # dependancies + nativeBuildInputs = godotLibraries.buildTools ++ godotLibraries.buildDep; + buildInputs = godotLibraries.runtimeDep; + runtimeDependencies = godotLibraries.runtimeDep; + # patch + patches = [ + ./patches/godot-cpp.patch # fix path for g++ + ]; + # build flags + sconsFlags = [ ("platfom=" + godotVersion.platform) ("target=" + target) "generate_bindings=true"] ++ godotCustom.customSconsFlags; + # maybe split outputs ["SConstruct" "binding_generator" ... ] + outputs = [ "out" ]; + installPhase = '' + mkdir -p $out + cp -r src $out/src + cp -r SConstruct $out/ + cp -r binding_generator.py $out/ + cp -r gdextension $out/ + cp -r include $out/ + cp -r tools $out/ + cp -r gen $out/ + chmod 755 $out -R + chmod 755 $out/gen/include/godot_cpp/core/ext_wrappers.gen.inc + ''; + } // args); + + + # function to build any GD-extension + buildExt = args @ { extName, version ? "0.1", src, target ? "editor", ... }: + let + # godot bindings for that extension + godotcpp = mkGodotCPP{inherit target;}; + in + stdenv.mkDerivation ({ + pname = extName + target; + version = version; + src = src; + nativeBuildInputs = nativeBuildInputs ++ [ godotcpp ]; + buildInputs = buildInputs; + runtimeDependencies = runtimeDependencies; + + # patch copies prebuilt godot-cpp + # there might be a smarter way to do this, but I'm dumb + # use Sconstruct from godotcpp + patchPhase = '' + mkdir -p godot-cpp + cp -r ${godotcpp}/* ./godot-cpp/ + chmod 777 -R godot-cpp + substituteInPlace SConstruct --replace 'env = SConscript("../SConstruct")' 'env = SConscript("godot-cpp/SConstruct")' + ''; + + sconsFlags = [ ("platfom=" + godotVersion.platform) ("target=" + target) "generate_bindings=true"] ++ godotCustom.customSconsFlags; + dontConfigure = true; + enableParallelBuilding = true; + + installPhase = '' + mkdir -p $out + ls -la > $out/files.txt + cp -r src $out/src + cp -r demo $out/ + cp -r godot-cpp $out/ + ''; + dontFixup = true; + } // args); +} diff --git a/flake.lock b/flake.lock index d8cd897..51de8d4 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "godot": { "flake": false, "locked": { - "lastModified": 1658153989, - "narHash": "sha256-X7Io/G1ASkkrJjgMwy7Gvwc2NAwF5fcWrrNcw6GD1hM=", + "lastModified": 1681495287, + "narHash": "sha256-Bi9jkewInrvASF5w/I9XdGzBDGYJkzyx3sWK5G8bbCw=", "owner": "godotengine", "repo": "godot", - "rev": "ee53a5161c8ce94ba2a06729d9d30099e74ba942", + "rev": "a7276f1ce0c2911216a2c4718efddab98ddffd8f", "type": "github" }, "original": { @@ -16,15 +16,30 @@ "type": "github" } }, - "nixpkgs": { + "godot-cpp": { + "flake": false, "locked": { - "lastModified": 1658119717, - "narHash": "sha256-4upOZIQQ7Bc4CprqnHsKnqYfw+arJeAuU+QcpjYBXW0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "9eb60f25aff0d2218c848dd4574a0ab5e296cabe", + "lastModified": 1680600608, + "narHash": "sha256-6+w6m+9dW6y6v7wcnL5ZM+q8NX6Kq/PSJ70crL/U+EQ=", + "owner": "godotengine", + "repo": "godot-cpp", + "rev": "feaba551b5a5b2d13ad1c3fdd8c90e67c67ff37c", "type": "github" }, + "original": { + "owner": "godotengine", + "repo": "godot-cpp", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678111249, + "narHash": "sha256-ZTIbK7vthZwti5XeLZE+twkb4l44q01q2XoLMmmJe94=", + "path": "/nix/store/h6mas49w0bwjxsql1fm9j7rr6jrdqvs4-source", + "rev": "a028e2873d7fcf44e66b784b4ba061824315537f", + "type": "path" + }, "original": { "id": "nixpkgs", "type": "indirect" @@ -33,6 +48,7 @@ "root": { "inputs": { "godot": "godot", + "godot-cpp": "godot-cpp", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index 24e89dc..d21baa0 100644 --- a/flake.nix +++ b/flake.nix @@ -1,60 +1,71 @@ +# Godot is a cross-platform open-source game engine written in C++ +# +# This flake build godot, the cpp bindings and the export templates +# { - inputs = { - godot.url = "github:godotengine/godot"; - godot.flake = false; + description = "the godot Engine, and the godot-cpp bindings for extensions"; + inputs = { + # the godot Engine + godot = { + url = "github:godotengine/godot"; + flake = false; }; - outputs = {self, nixpkgs, ...}@inputs: - let - system = "x86_64-linux"; - pkgs = import nixpkgs{inherit system;}; - in - rec{ - packages."${system}" = with pkgs; { - default = stdenv.mkDerivation rec{ - name = "godot"; - src = inputs.godot; - nativeBuildInputs = [ - scons - pkg-config - vulkan-loader - xorg.libX11 - xorg.libXcursor - xorg.libXinerama - xorg.libXrandr - xorg.libXrender - xorg.libXi - xorg.libXext - xorg.libXfixes - udev - systemd - systemd.dev - libpulseaudio - freetype - openssl - alsa-lib - libGLU - zlib - yasm - autoPatchelfHook - ]; - runtimeDependencies = [vulkan-loader libpulseaudio]; - patchPhase = '' - substituteInPlace platform/linuxbsd/detect.py --replace 'pkg-config xi ' 'pkg-config xi xfixes ' - ''; - enableParallelBuilding = true; - buildInputs = nativeBuildInputs; - - sconsFlags = "platform=linuxbsd"; - installPhase = '' - mkdir -p "$out/bin" - cp bin/godot.* $out/bin/godot - ''; - }; - }; - devShells."${system}".head = with pkgs; mkShell{ - nativeBuildInputs = [patchelf nodePackages.http-server]; - runtimeDependencies = nativeBuildInputs; - }; + # the godot cpp bindings to build GDExtensions + godot-cpp = { + url = "github:godotengine/godot-cpp"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let + # only linux supported + # TODO: support darwin and cross compilation + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + lib = pkgs.lib; + + # helper function + buildGodot = import ./godot.nix { inherit lib pkgs system inputs; }; + buildGdExt = import ./extensions.nix { inherit lib pkgs system inputs; }; + + # godot engine + godot-editor = buildGodot.mkGodot { }; # Godot Editor + godot-template-release = + buildGodot.mkGodotTemplate { target = "template_release"; }; + godot-template-debug = + buildGodot.mkGodotTemplate { target = "template_debug"; }; + + # whole godot package + godot-engine = pkgs.buildEnv { + name = "godot-engine"; + paths = [ godot-editor godot-template-release godot-template-debug ]; + }; + + # godot cpp bindings + godot-cpp-editor = buildGdExt.mkGodotCPP { target = "editor"; }; + + # extension demo + godot-cpp-demo = buildGdExt.buildExt { + extName = "godot-cpp-demo"; + src = "${inputs.godot-cpp}/test"; + }; + + in { + + # build functions : + lib = { inherit buildGodot buildGdExt; }; + #packages + packages."${system}" = with pkgs; { + default = pkgs.linkFarmFromDrvs "godot-flake" [ + godot-engine + godot-cpp-editor + godot-cpp-demo + ]; + }; + # dev-shell + # TODO : Godot development tools + devShells."${system}".default = with pkgs; mkShell { }; }; } diff --git a/godot.nix b/godot.nix new file mode 100644 index 0000000..ac8cebe --- /dev/null +++ b/godot.nix @@ -0,0 +1,101 @@ +# Godot.nix +# this modules focuses on building godot +# TODO : Add support for a custom.py +{ lib, pkgs, inputs, system }: +with pkgs; +with builtins; +let + + # godot version + godotVersion = import ./version.nix { inherit system; }; + # godot custom.py + godotCustom = import ./custom.nix { inherit lib system; }; + # godot build libraries + godotLibraries = import ./libs.nix { + inherit pkgs; + use_x11 = true; + use_mono = false; + }; + + # default installation for godot engine + defaultInstall = '' + mkdir -p "$out/bin" + cp bin/godot.* $out/bin/godot-${godotVersion.version}-${target} + ''; + + # function to make a godot derivation + mkGodotBase = {pname ? "godot", target ? "editor", tools ? true, installPhase ? defaultInstall, strip ? []} : stdenv.mkDerivation { + + # pass parameters + inherit installPhase strip; + + # use variables from args + name = (concatStringsSep "-" [pname target godotVersion.version]); + src = inputs.godot; + + # get godot version from version modules + version = godotVersion.version; + platform = godotVersion.platform; + + # As a rule of thumb: Buildtools as nativeBuildInputs, + # libraries and executables you only need after the build as buildInputs + nativeBuildInputs = godotLibraries.buildDep ++ godotLibraries.buildTools; + buildInputs = godotLibraries.runtimeDep; + runtimeDependencies = godotLibraries.runtimeDep; + enableParallelBuilding = true; + + # scons flags list + sconsFlags = [ + ("platfom=" + godotVersion.platform) + ("target=" + target) + (if tools then "tools=yes" else "tools=no") + ("use_sowrap=false") # make sure to link to system libraries + ("use_volk=false") # Get vulkan via system libraries + ] ++ godotCustom.customSconsFlags; + + # apply the necessary patches + patches = [ + ./patches/xfixes.patch # fix x11 libs + ./patches/gl.patch # fix gl libs + ]; + + # some extra info + meta = with lib; { + homepage = pkgs.godot.meta.homepage; + description = pkgs.godot.meta.description; + license = licenses.mit; + }; + }; + +# implementation +in { + # mkGodot + # function to male a godot build + mkGodot = {target ? "editor"}: mkGodotBase { + inherit target; + tools = true; + installPhase = '' + mkdir -p "$out/bin" + cp bin/godot.* $out/bin/godot-${godotVersion.version}-${target} + mkdir -p "$out"/share/{applications,icons/hicolor/scalable/apps} + cp misc/dist/linux/org.godotengine.Godot.desktop "$out/share/applications/" + substituteInPlace "$out/share/applications/org.godotengine.Godot.desktop" \ + --replace "Exec=godot" "Exec=$out/bin/godot" + cp icon.svg "$out/share/icons/hicolor/scalable/apps/godot.svg" + cp icon.png "$out/share/icons/godot.png" + ''; + # Do not set GODOT4_BIN=out/bin/godot-${target} because we may build templates toos + }; + + # build a template + mkGodotTemplate = {target ? "template_debug"} : mkGodotBase { + inherit target; + tools = false; + installPhase = '' + mkdir -p "$out/share/godot/templates/${godotVersion.version}" + cp bin/godot.* $out/share/godot/templates/${godotVersion.version}/${godotVersion.platform}-${target} + ''; + # https://docs.godotengine.org/en/stable/development/compiling/optimizing_for_size.html + #strip = (oldAttrs.stripAllList or [ ]) ++ [ "share/godot/templates" ]; + }; +} \ No newline at end of file diff --git a/libs.nix b/libs.nix new file mode 100644 index 0000000..bb4dd59 --- /dev/null +++ b/libs.nix @@ -0,0 +1,61 @@ +# libs.nix +# Godot libraries for build and runtime +{ pkgs, use_llvm ? true, use_x11 ? true, use_openGL ? true, use_mono ? true }: +let + + conditionalLib = c: l: (if (c == true) then l else [ ]); + + # mono/C# + libMono = with pkgs; [ mono6 msbuild dotnetPackages.Nuget ]; + # llvm compiler + libllvm = with pkgs; [ llvm lld clang ]; + # x11 libraries + libXorg = with pkgs.xorg; [ + libX11 + libXcursor + libXi + libXinerama + libXrandr + libXrender + libXext + libXfixes + ]; + + # OpenGL libraries + libOpenGL = with pkgs; [ glslang libGLU libGL ]; + +in { + + buildTools = with pkgs; + [ + scons + pkg-config + installShellFiles + autoPatchelfHook + bashInteractive + patchelf + gcc + ] ++ conditionalLib use_llvm libllvm; + + # runtime dependencies + runtimeDep = with pkgs; + [ + udev + systemd + systemd.dev + libpulseaudio + freetype + openssl + alsa-lib + fontconfig.lib + speechd + libxkbcommon + dbus.lib + vulkan-loader + ] ++ conditionalLib use_x11 libXorg; + + # build dependancies + buildDep = with pkgs; + [ zlib yasm vulkan-headers ] ++ conditionalLib use_x11 libXorg + ++ conditionalLib use_openGL libOpenGL ++ conditionalLib use_mono libMono; +} diff --git a/patches/gl.patch b/patches/gl.patch new file mode 100644 index 0000000..7244000 --- /dev/null +++ b/patches/gl.patch @@ -0,0 +1,13 @@ +diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py +index 3f713d2db3..b569db6e12 100644 +--- a/platform/linuxbsd/detect.py ++++ b/platform/linuxbsd/detect.py +@@ -420,6 +420,8 @@ def configure(env: "Environment"): + + if env["opengl3"]: + env.Append(CPPDEFINES=["GLES3_ENABLED"]) ++ env.ParseConfig("pkg-config glu --cflags --libs") ++ env.ParseConfig("pkg-config zlib --cflags --libs") + + env.Append(LIBS=["pthread"]) + diff --git a/patches/godot-cpp.patch b/patches/godot-cpp.patch new file mode 100644 index 0000000..871a498 --- /dev/null +++ b/patches/godot-cpp.patch @@ -0,0 +1,13 @@ +diff --git a/SConstruct b/SConstruct +index 27ee137..10fe07d 100644 +--- a/SConstruct ++++ b/SConstruct +@@ -53,7 +53,7 @@ else: + + # Default tools with no platform defaults to gnu toolchain. + # We apply platform specific toolchains via our custom tools. +-env = Environment(tools=["default"], PLATFORM="") ++env = Environment(tools=["default"], PLATFORM="", ENV={"PATH" : os.environ["PATH"]}) + + # Default num_jobs to local cpu count if not user specified. + # SCons has a peculiarity where user-specified options won't be overridden diff --git a/patches/xfixes.patch b/patches/xfixes.patch new file mode 100644 index 0000000..df47919 --- /dev/null +++ b/patches/xfixes.patch @@ -0,0 +1,15 @@ +diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py +index 3f713d2db3..74217ce608 100644 +--- a/platform/linuxbsd/detect.py ++++ b/platform/linuxbsd/detect.py +@@ -408,6 +408,10 @@ def configure(env: "Environment"): + print("Error: Xi library not found. Aborting.") + sys.exit(255) + env.ParseConfig("pkg-config xi --cflags --libs") ++ if os.system("pkg-config --exists xfixes"): ++ print("Error: Xfixes library not found. Aborting.") ++ sys.exit(255) ++ env.ParseConfig("pkg-config xfixes --cflags --libs") + env.Append(CPPDEFINES=["X11_ENABLED"]) + + if env["vulkan"]: diff --git a/version.nix b/version.nix new file mode 100644 index 0000000..609ade8 --- /dev/null +++ b/version.nix @@ -0,0 +1,8 @@ +# version.nix +# get godot version +{ system }: { + # this builds godot 4. we should instead read it from the input file + version = "4.1-dev"; + # todo : test darwin support + platform = if (system == "x86_64-linux") then "linuxbsd" else "darwin"; +}