From f7f4c52d3df2a7327130f76c88dab685851d1d99 Mon Sep 17 00:00:00 2001 From: Naxdy Date: Tue, 4 Feb 2025 16:59:31 +0100 Subject: [PATCH] Add nix flake + package definitions for editor & export templates --- .envrc | 1 + .gitignore | 5 + SConstruct | 3 + export-template-package.nix | 116 +++++++++++++++++ flake.lock | 27 ++++ flake.nix | 113 +++++++++++++++++ package.nix | 240 ++++++++++++++++++++++++++++++++++++ platform/windows/detect.py | 4 + 8 files changed, 509 insertions(+) create mode 100644 .envrc create mode 100644 export-template-package.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package.nix diff --git a/.envrc b/.envrc new file mode 100644 index 00000000000..3550a30f2de --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 8adc7b786c6..3f0b0d205b7 100644 --- a/.gitignore +++ b/.gitignore @@ -386,3 +386,8 @@ $RECYCLE.BIN/ *.msp *.lnk *.generated.props + +# Nix +.direnv/ +/result +/result-* diff --git a/SConstruct b/SConstruct index 3d0d4b3918a..96c83839f6a 100644 --- a/SConstruct +++ b/SConstruct @@ -299,6 +299,9 @@ opts.Add("rcflags", "Custom flags for Windows resource compiler") opts.Add("c_compiler_launcher", "C compiler launcher (e.g. `ccache`)") opts.Add("cpp_compiler_launcher", "C++ compiler launcher (e.g. `ccache`)") +# Nix +opts.Add(BoolVariable("nix", "Whether the project is built using Nix.", False)) + # Update the environment to have all above options defined # in following code (especially platform and custom_modules). opts.Update(env) diff --git a/export-template-package.nix b/export-template-package.nix new file mode 100644 index 00000000000..e43262474dc --- /dev/null +++ b/export-template-package.nix @@ -0,0 +1,116 @@ +{ + pkgsCross, + symlinkJoin, + lib, + redot, +}: +{ + templateType ? "release", + platform ? "linuxbsd", +}: +let + exportPlatformNames = { + linuxbsd = "linux"; + windows = "windows"; + }; + + mkTemplate = + { + pkg, + templateType ? "release", + platform ? "linuxbsd", + arch ? "x86_64", + }: + let + basePkg = + (pkg.override { + withTarget = "template_${templateType}"; + withPlatform = platform; + }).overrideAttrs + (old: { + pname = "redot4-export-templates-${platform}-${templateType}"; + + outputs = [ "out" ]; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/share/redot/export_templates/${old.redotVersion}" + + cp bin/redot.* "$out/share/redot/export_templates/${old.redotVersion}/${ + exportPlatformNames.${platform} + }_${templateType}_${arch}" + + runHook postInstall + ''; + }); + in + ( + if platform == "windows" then + mkWindowsTemplate { + inherit templateType; + pkg = basePkg; + } + else + basePkg + ); + + mkWindowsTemplate = + { + pkg, + templateType ? "release", + }: + let + arch = "x86_64"; + in + (pkg.override { + withPlatform = "windows"; + inherit arch; + importEnvVars = [ + "CPLUS_INCLUDE_PATH" + ]; + }).overrideAttrs + ( + old: + let + buildInputs = (old.buildInputs or [ ]) ++ [ + pkgsCross.mingwW64.windows.mingw_w64_pthreads + pkgsCross.mingwW64.windows.mcfgthreads + pkgsCross.mingw32.windows.mcfgthreads + ]; + + libs = symlinkJoin { + name = "redot-export-templates-windows-libpath"; + paths = buildInputs; + }; + in + { + nativeBuildInputs = old.nativeBuildInputs ++ [ + pkgsCross.mingwW64.buildPackages.gcc + pkgsCross.mingw32.buildPackages.gcc + ]; + + inherit buildInputs; + + env = (old.env or { }) // { + CPLUS_INCLUDE_PATH = lib.makeIncludePath buildInputs; + NIX_LIBS = "${libs}/lib"; + }; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/share/redot/export_templates/${old.redotVersion}" + + cp bin/redot.*.console.exe "$out/share/redot/export_templates/${old.redotVersion}/windows_${templateType}_${arch}_console.exe" + cp bin/redot.*.${arch}.exe "$out/share/redot/export_templates/${old.redotVersion}/windows_${templateType}_${arch}.exe" + + runHook postInstall + ''; + } + ); +in +mkTemplate { + inherit platform templateType; + pkg = redot; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000000..de56abfde07 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1750506804, + "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4206c4cb56751df534751b058295ea61357bbbaa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000000..d781d198420 --- /dev/null +++ b/flake.nix @@ -0,0 +1,113 @@ +{ + description = "Redot Game Engine"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { + self, + nixpkgs, + }: + let + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + forEachSupportedSystem = + f: + nixpkgs.lib.genAttrs supportedSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ + self.overlays.default + ]; + }; + in + f { + inherit pkgs system; + } + ); + in + { + packages = forEachSupportedSystem ( + { + pkgs, + system, + ... + }: + { + default = self.packages.${system}.redot; + + inherit (pkgs) + redot + redot-export-templates + redot-export-templates-linux-debug + redot-export-templates-linux-release + redot-export-templates-windows-debug + redot-export-templates-windows-release + ; + } + ); + + overlays.default = final: prev: { + mkExportTemplate = final.callPackage (import ./export-template-package.nix) { }; + + redot = final.callPackage (import ./package.nix) { + src = self; + commitHash = if self ? rev then self.rev else "devel"; + }; + + redot-export-templates-linux-release = final.mkExportTemplate { + templateType = "release"; + platform = "linuxbsd"; + }; + + redot-export-templates-linux-debug = final.mkExportTemplate { + templateType = "debug"; + platform = "linuxbsd"; + }; + + redot-export-templates-windows-release = final.mkExportTemplate { + templateType = "release"; + platform = "windows"; + }; + + redot-export-templates-windows-debug = final.mkExportTemplate { + templateType = "debug"; + platform = "windows"; + }; + + redot-export-templates = final.symlinkJoin { + name = "redot-export-templates"; + paths = builtins.attrValues { + inherit (final) + redot-export-templates-linux-debug + redot-export-templates-linux-release + redot-export-templates-windows-debug + redot-export-templates-windows-release + ; + }; + }; + }; + + devShells = forEachSupportedSystem ( + { pkgs, system, ... }: + { + default = pkgs.mkShell { + inputsFrom = [ + self.packages.${system}.redot + ]; + + packages = self.packages.${system}.redot.runtimeDependencies; + }; + } + ); + }; +} diff --git a/package.nix b/package.nix new file mode 100644 index 00000000000..c407f2efccd --- /dev/null +++ b/package.nix @@ -0,0 +1,240 @@ +# Based on https://github.com/NixOS/nixpkgs/blob/6d5b297bc68f021d5fe9d43a3d1ba35a3b6d0663/pkgs/by-name/go/godot_4/package.nix + +{ + alsa-lib, + autoPatchelfHook, + buildPackages, + dbus, + fontconfig, + installShellFiles, + lib, + libdecor, + libGL, + libpulseaudio, + libX11, + libXcursor, + libXext, + libXfixes, + libXi, + libXinerama, + libxkbcommon, + libXrandr, + libXrender, + pkg-config, + scons, + speechd-minimal, + stdenv, + testers, + udev, + vulkan-loader, + wayland, + wayland-scanner, + withDbus ? true, + withFontconfig ? true, + withPlatform ? "linuxbsd", + withPrecision ? "single", + withPulseaudio ? true, + withSpeechd ? true, + withTarget ? "editor", + withTouch ? true, + withUdev ? true, + # Wayland in Redot requires X11 until upstream fix is merged + # https://github.com/godotengine/godot/pull/73504 + withWayland ? true, + withX11 ? true, + src, + commitHash, + arch ? stdenv.hostPlatform.linuxArch, + importEnvVars ? [ ], +}: +assert lib.asserts.assertOneOf "withPrecision" withPrecision [ + "single" + "double" +]; +let + mkSconsFlagsFromAttrSet = lib.mapAttrsToList ( + k: v: if builtins.isString v then "${k}=${v}" else "${k}=${builtins.toJSON v}" + ); + + versionInfo = builtins.listToAttrs ( + map ( + e: + let + valuePair = lib.splitString " = " e; + in + { + name = builtins.elemAt valuePair 0; + value = lib.replaceStrings [ "\"" ] [ "" ] (builtins.elemAt valuePair 1); + } + ) (lib.splitString "\n" (builtins.readFile ./version.py)) + ); + + version = "${versionInfo.major}.${versionInfo.minor}${ + lib.optionalString (versionInfo.patch != "0") ".${versionInfo.patch}" + }-${versionInfo.status}"; + + redotVersion = lib.replaceStrings [ "-" ] [ "." ] version; +in +stdenv.mkDerivation (finalAttrs: { + pname = "redot4"; + + inherit src version redotVersion; + + outputs = [ + "out" + "man" + ]; + separateDebugInfo = true; + + # Set the build name which is part of the version. In official downloads, this + # is set to 'official'. When not specified explicitly, it is set to + # 'custom_build'. Other platforms packaging Redot (Gentoo, Arch, Flatpak, NixOS + # etc.) usually set this to their name as well. + # + # See also 'methods.py' in the Redot repo and 'build' in + # https://docs.redotengine.org/en/stable/classes/class_engine.html#class-engine-method-get-version-info + BUILD_NAME = "flake"; + + # Required for the commit hash to be included in the version number. + # + # `methods.py` reads the commit hash from `.git/HEAD` and manually follows + # refs. Since we just write the hash directly, there is no need to emulate any + # other parts of the .git directory. + # + # See also 'hash' in + # https://docs.redotengine.org/en/stable/classes/class_engine.html#class-engine-method-get-version-info + preConfigure = '' + mkdir -p .git + echo ${commitHash} > .git/HEAD + ''; + + # From: https://github.com/Redot-Engine/redot-engine/blob/redot-4.3-stable/SConstruct + sconsFlags = mkSconsFlagsFromAttrSet ( + { + # Options from 'SConstruct' + precision = withPrecision; # Floating-point precision level + production = true; # Set defaults to build Redot for use in production + platform = withPlatform; + target = withTarget; + import_env_vars = lib.foldl (a: b: "${a},${b}") "" importEnvVars; + nix = true; + + inherit arch; + } + // (lib.optionalAttrs (withTarget == "editor") { + # Options from 'platform/linuxbsd/detect.py' + dbus = withDbus; # Use D-Bus to handle screensaver and portal desktop settings + fontconfig = withFontconfig; # Use fontconfig for system fonts support + pulseaudio = withPulseaudio; # Use PulseAudio + speechd = withSpeechd; # Use Speech Dispatcher for Text-to-Speech support + touch = withTouch; # Enable touch events + udev = withUdev; # Use udev for gamepad connection callbacks + wayland = withWayland; # Compile with Wayland support + x11 = withX11; # Compile with X11 support + module_mono_enabled = false; + + linkflags = "-Wl,--build-id"; + + debug_symbols = true; + }) + ); + + enableParallelBuilding = true; + + strictDeps = true; + + depsBuildBuild = lib.optionals (stdenv.buildPlatform != stdenv.hostPlatform) [ + buildPackages.stdenv.cc + pkg-config + ]; + + nativeBuildInputs = [ + autoPatchelfHook + installShellFiles + pkg-config + scons + ] ++ lib.optionals withWayland [ wayland-scanner ]; + + runtimeDependencies = + [ + alsa-lib + libGL + vulkan-loader + ] + ++ lib.optionals withX11 [ + libX11 + libXcursor + libXext + libXfixes + libXi + libXinerama + libxkbcommon + libXrandr + libXrender + ] + ++ lib.optionals withWayland [ + libdecor + wayland + ] + ++ lib.optionals withDbus [ + dbus + dbus.lib + ] + ++ lib.optionals withFontconfig [ + fontconfig + fontconfig.lib + ] + ++ lib.optionals withPulseaudio [ libpulseaudio ] + ++ lib.optionals withSpeechd [ speechd-minimal ] + ++ lib.optionals withUdev [ udev ]; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/bin" + cp bin/redot.* $out/bin/redot4 + + installManPage misc/dist/linux/redot.6 + + mkdir -p "$out"/share/{applications,icons/hicolor/scalable/apps} + cp misc/dist/linux/org.redotengine.Redot.desktop "$out/share/applications/org.redotengine.Redot4.desktop" + substituteInPlace "$out/share/applications/org.redotengine.Redot4.desktop" \ + --replace "Exec=redot" "Exec=$out/bin/redot4" \ + --replace "Redot Engine" "Redot Engine 4" + cp icon.svg "$out/share/icons/hicolor/scalable/apps/redot.svg" + cp icon.png "$out/share/icons/redot.png" + + runHook postInstall + ''; + + # see https://github.com/NixOS/nixpkgs/pull/400347 + dontAutoPatchelf = true; + + postFixup = '' + autoPatchelf "$out" + ''; + + passthru.tests = { + version = testers.testVersion { + package = finalAttrs.finalPackage; + version = redotVersion; + }; + }; + + requiredSystemFeatures = [ + # fixes: No space left on device + "big-parallel" + ]; + + meta = { + changelog = "https://github.com/Redot-Engine/redot-engine/releases/tag/${version}"; + description = "Free and Open Source 2D and 3D game engine"; + homepage = "https://redotengine.org"; + license = lib.licenses.mit; + platforms = [ + "x86_64-linux" + "aarch64-linux" + ]; + mainProgram = "redot4"; + }; +}) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 8f726dd7715..596b0b3fdb4 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -676,6 +676,10 @@ def configure_mingw(env: "SConsEnvironment"): ## Build type + # Need this under Nix to find libraries like mcfgthread + if env["nix"]: + env.Append(LIBPATH=[os.environ.get("NIX_LIBS")]) + if not env["use_llvm"] and not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]): env["use_llvm"] = True