diff --git a/pkgs/by-name/re/renpy/distribute.patch b/pkgs/by-name/re/renpy/distribute.patch new file mode 100644 index 000000000000..c0bceb135919 --- /dev/null +++ b/pkgs/by-name/re/renpy/distribute.patch @@ -0,0 +1,13 @@ +diff --git a/launcher/game/distribute.rpy b/launcher/game/distribute.rpy +index bece65c63..831c405f8 100644 +--- a/launcher/game/distribute.rpy ++++ b/launcher/game/distribute.rpy +@@ -863,7 +863,7 @@ fix_dlc("renios", "renios") + ) + + for fn in os.listdir(directory): +- walk(fn, os.path.join(directory, fn)) ++ walk(fn, os.path.join(directory, "renpy-dist" if fn == "renpy" else fn)) + + def merge_file_lists(self): + """ diff --git a/pkgs/by-name/re/renpy/dont-compile-system.patch b/pkgs/by-name/re/renpy/dont-compile-system.patch new file mode 100644 index 000000000000..81d45adacf95 --- /dev/null +++ b/pkgs/by-name/re/renpy/dont-compile-system.patch @@ -0,0 +1,31 @@ +diff --git a/renpy/script.py b/renpy/script.py +index 62e424415..7c3ae5922 100644 +--- a/renpy/script.py ++++ b/renpy/script.py +@@ -969,6 +969,13 @@ class Script(object): + f.seek(-hashlib.md5().digest_size, 2) + digest = f.read(hashlib.md5().digest_size) + ++ elif dir.startswith("@systemRenpy@"): ++ data, stmts = self.load_file(dir, fn + compiled) ++ lastfn = dir + "/" + fn + compiled ++ with open(lastfn, "rb") as f: ++ f.seek(-hashlib.md5().digest_size, 2) ++ digest = f.read(hashlib.md5().digest_size) ++ + else: + # Otherwise, we're loading from disk. So we need to decide if + # we want to load the rpy or the rpyc file. +diff --git a/launcher/game/distribute.rpy b/launcher/game/distribute.rpy +index bece65c63..c2590bcf7 100644 +--- a/launcher/game/distribute.rpy ++++ b/launcher/game/distribute.rpy +@@ -606,7 +606,7 @@ fix_dlc("renios", "renios") + self.log.close() + return + +- if project.data['force_recompile']: ++ if project.data['force_recompile'] and False: + import compileall + + compileall.compile_dir( diff --git a/pkgs/by-name/re/renpy/new-project-prefix.patch b/pkgs/by-name/re/renpy/new-project-prefix.patch new file mode 100644 index 000000000000..86d14fdf2d44 --- /dev/null +++ b/pkgs/by-name/re/renpy/new-project-prefix.patch @@ -0,0 +1,30 @@ +diff --git a/launcher/game/gui7/code.py b/launcher/game/gui7/code.py +index 5c1b89f23c..c8ad04b6dd 100644 +--- a/launcher/game/gui7/code.py ++++ b/launcher/game/gui7/code.py +@@ -243,9 +243,7 @@ def quote(s): + + self.update_defines(replacements) + +- def write_target(self, filename): +- +- target = os.path.join(self.p.prefix, filename) ++ def write_target(self, target): + + if os.path.exists(target): + +@@ -421,7 +419,7 @@ def generate_gui(self, fn, defines=False): + self.translate_comments() + self.add_code(fn) + +- self.write_target(fn) ++ self.write_target(os.path.join(self.p.prefix, fn)) + + def generate_code(self, fn): + +@@ -439,4 +437,4 @@ def generate_code(self, fn): + + self.add_code(fn) + +- self.write_target(fn) ++ self.write_target(target) diff --git a/pkgs/by-name/re/renpy/noSteam.patch b/pkgs/by-name/re/renpy/noSteam.patch deleted file mode 100644 index 6322bcff3248..000000000000 --- a/pkgs/by-name/re/renpy/noSteam.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/renpy/common/00steam.rpy b/renpy/common/00steam.rpy -index 9a5f9c405..68c8c26e0 100644 ---- a/renpy/common/00steam.rpy -+++ b/renpy/common/00steam.rpy -@@ -1029,11 +1029,6 @@ init -1499 python in achievement: - steam = None - steamapi = None - -- if renpy.windows or renpy.macintosh or renpy.linux: -- steam_preinit() -- steam_init() -- -- - init 1500 python in achievement: - - # Steam position. diff --git a/pkgs/by-name/re/renpy/package.nix b/pkgs/by-name/re/renpy/package.nix index b99aa9d32ce1..0d462441f26f 100644 --- a/pkgs/by-name/re/renpy/package.nix +++ b/pkgs/by-name/re/renpy/package.nix @@ -1,45 +1,110 @@ { + lib, + stdenv, assimp, + copyDesktopItems, + desktopToDarwinBundle, fetchFromGitHub, + fetchurl, + fetchzip, ffmpeg, freetype, fribidi, glew, harfbuzz, - lib, libGL, libGLU, + libjpeg, libpng, - makeWrapper, + makeBinaryWrapper, + makeDesktopItem, + openssl, pkg-config, python312, SDL2, - stdenv, + SDL2_image, versionCheckHook, - withoutSteam ? true, + zenity, zlib, + + # the minimal package contains only compiled python and cython files, and the example projects and the launcher are removed + # one should use the minimal package in favor of the full package when packaging games, in which case only the game runtime is needed + minimal ? false, + # with this, you can click "Documentation" in the launcher to open local doc (otherwise it opens web doc) + withDoc ? !minimal, + # set this to true if you want to use this package to distribute games + # (to windows, linux, and macos, outside of nix; android, ios, and web are not supported) + withDistributedLibs ? !minimal, + # set this to true if you additionally want to distribute games for aarch64-linux + # this implies withDistributedLibs = true because it also includes the libraries for other platforms + withAarch64LinuxDistributedLibs ? + withDistributedLibs && stdenv.targetPlatform.isAarch64 && stdenv.targetPlatform.isLinux, }: +# technically we can support cross-compilation by first compiling a renpy for the build platform besides a renpy for the host platform +# and we can use the former to compile the rpy{,m} files but install the latter to $out +# but let's not bother +assert lib.assertMsg (stdenv.buildPlatform.canExecute stdenv.hostPlatform) + "Ren'Py cannot be cross-compiled because it needs to run itself during the build phase."; + +assert lib.assertMsg (!minimal || !withDistributedLibs && !withAarch64LinuxDistributedLibs) + "The distributed libraries are only useful when used with the Ren'Py launcher, which is not installed for the minimal Ren'Py package."; + let - python = python312; + pythonBuildTime = python312.withPackages ( + ps: + with ps; + [ + cython + setuptools + pkgconfig + + # the runtime dependencies are also added to compile bundled rpy{,m} files in renpy source tree + future + pefile + requests + rsa + six + ] + ++ lib.optionals withDoc [ + sphinx + sphinx-rtd-theme + sphinx-rtd-dark-mode + ] + ); + pythonRunTime = python312.withPackages ( + ps: with ps; [ + future + pefile + requests + rsa + six + ] + ); + in stdenv.mkDerivation (finalAttrs: { pname = "renpy"; - version = "8.4.1.25072401"; + # unstable version drops dependency on insecure package ecdsa + version = "8.5.2.26010301-unstable-2026-03-27"; src = fetchFromGitHub { owner = "renpy"; repo = "renpy"; - tag = finalAttrs.version; - hash = "sha256-wJnMqUrRGWcsuZWdqbiUI/BD2sSRjJKEzsCOzSngoZM="; + rev = "09eb6986ea9e5dbe64c9096ed48a638e593ea0ef"; + hash = "sha256-w7tQbZCH7F0Npu8rD2UADxe/KzsTUdtIhJY6GH4YFAs="; }; + __structuredAttrs = true; + strictDeps = true; + nativeBuildInputs = [ - makeWrapper + makeBinaryWrapper pkg-config - python.pkgs.cython - python.pkgs.setuptools - ]; + pythonBuildTime + ] + ++ lib.optional (!minimal) copyDesktopItems + ++ lib.optional (stdenv.hostPlatform.isDarwin && !minimal) desktopToDarwinBundle; buildInputs = [ assimp @@ -50,86 +115,170 @@ stdenv.mkDerivation (finalAttrs: { harfbuzz libGL libGLU - libpng + libjpeg + openssl SDL2 - zlib - ] - ++ (with python.pkgs; [ - ecdsa - future - pefile - pygame-sdl2 - python - requests - six - tkinter - ]); - - env = { - NIX_CFLAGS_COMPILE = "-I${python.pkgs.pygame-sdl2}/include"; - - RENPY_DEPS_INSTALL = lib.concatStringsSep "::" ( - [ - ffmpeg.lib - freetype - fribidi - glew.dev - harfbuzz.dev - libpng - SDL2 - (lib.getDev SDL2) - zlib - ] - ++ lib.optionals (!stdenv.hostPlatform.isDarwin) [ - libGL - libGLU - ] - ); - }; + SDL2_image + pythonRunTime + ]; enableParallelBuilding = true; patches = [ - ./shutup-erofs-errors.patch - ] - ++ lib.optional withoutSteam ./noSteam.patch; + # do not try to compile renpy files installed in nix store because we already compiled them at build phase + ./dont-compile-system.patch + + # catch error instead of crashing when trying to write steam_appid.txt to nix store + # https://github.com/renpy/renpy/pull/6976 + ./steam-preinit-catch.patch + + # fix write_target looking for wrong file locations when launcher creates new project + # https://github.com/renpy/renpy/pull/6978 + ./new-project-prefix.patch + + # the distributed libs are not compatible with renpy built from source, + # so patch the launcher to look for renpy files in renpy-dist (where bin distribution from upstream is copied to) instead of renpy + ./distribute.patch + ]; postPatch = '' + # use nix out path instead of `renpy.config.renpy_base` because otherwise we cannot compile them in the build phase + substituteInPlace renpy/script.py --replace-fail "@systemRenpy@" "$out/share/renpy" + + patchShebangs --build setup.py + + # https://github.com/renpy/renpy/blob/8.5.2.26010301/tutorial/game/01director_support.rpy cp tutorial/game/tutorial_director.rpy{m,} cat > renpy/vc_version.py << EOF - version = '${finalAttrs.version}' + branch = 'master' + version = '${finalAttrs.passthru.appver}' official = False nightly = False # Look at https://renpy.org/latest.html for what to put. - version_name = "Tomorrowland" + version_name = "In Good Health" EOF ''; + env.PYTHONDONTWRITEBYTECODE = "1"; + buildPhase = '' runHook preBuild - ${python.pythonOnBuildForHost.interpreter} setup.py build --parallel=$NIX_BUILD_CORES + + ./setup.py build_ext --inplace -j $NIX_BUILD_CORES + + # so that these files won't need to be compiled on the host platform + python -m compileall renpy -q -d renpy -f${lib.optionalString minimal " -b"}; + + ${lib.optionalString (!minimal) '' + # compile bundled rpy{,m} files so that they don't have to be compiled when used on the host platform + python renpy.py gui compile + python renpy.py tutorial compile + python renpy.py the_question compile + ''} + + # there is no single command to compile all rpym files, so apply a temporary patch for doing that + patch -p1 -i ${./temp-compile-modules.patch} + python renpy.py . compile + patch -p1 -R -i ${./temp-compile-modules.patch} + + ${lib.optionalString (!minimal) "rm -r {tutorial,the_question}/game/saves"} + + ${lib.optionalString withDoc '' + # https://github.com/renpy/renpy/blob/8.5.2.26010301/sphinx/build.sh + pushd sphinx + mkdir -p source/inc + python ../renpy.py . + RENPY_NO_FIGURES=1 sphinx-build -E -a source ../doc + popd + ''} + runHook postBuild ''; installPhase = '' runHook preInstall - ${python.pythonOnBuildForHost.interpreter} setup.py install_lib -d $out/${python.sitePackages} - mkdir -p $out/share/renpy - cp -vr sdk-fonts gui launcher renpy the_question tutorial renpy.py $out/share/renpy + ./setup.py install_lib -d $out/share/renpy + cp -ar renpy renpy.py $out/share/renpy - makeWrapper ${python.interpreter} $out/bin/renpy \ - --set PYTHONPATH "$PYTHONPATH:$out/${python.sitePackages}" \ - --add-flags "$out/share/renpy/renpy.py" + makeWrapper ${lib.getExe pythonRunTime} $out/bin/renpy --add-flags "$out/share/renpy/renpy.py" ${ + # add zenity for file dialogs (https://github.com/renpy/renpy/blob/8.5.2.26010301/src/tinyfiledialogs/tinyfiledialogs.c#L188) + lib.optionalString (!minimal) "--prefix PATH : ${lib.makeBinPath [ zenity ]}" + } + + ${lib.optionalString minimal '' + # delete files not necessary at runtime + find $out/share/renpy/renpy -type f -regextype posix-egrep -regex '.*\.(py|pyx|pyd|pxd|pyi|pxi|rpy|rpym)$' -delete + ''} + + ${lib.optionalString (!minimal) '' + cp -ar sdk-fonts gui launcher the_question tutorial $out/share/renpy + + # most commands (such as `distribute`) are commands of the launcher but not renpy itself + makeWrapper $out/bin/renpy $out/bin/renpy-launcher --add-flags "$out/share/renpy/launcher" + + mkdir -p $out/share/icons/hicolor/{256x256,32x32}/apps + ln -s $out/share/renpy/launcher/game/images/window-icon.png $out/share/icons/hicolor/256x256/apps/renpy.png + ln -s $out/share/renpy/launcher/game/images/logo32.png $out/share/icons/hicolor/32x32/apps/renpy.png + ''} + + ${lib.optionalString withDoc "cp -ar doc $out/share/renpy"} + + ${lib.optionalString (finalAttrs.passthru.distributedRenpy != null) '' + # have to use cp instead of symlinkJoin because renpy resolves symlinks to find its base dir + cp -ar ${finalAttrs.passthru.distributedRenpy}/{update,lib,renpy.sh} $out/share/renpy + # renpy packaged from source in this nix package is not compatible with the distributed libs + cp -ar ${finalAttrs.passthru.distributedRenpy}/renpy $out/share/renpy/renpy-dist + ''} runHook postInstall ''; - nativeInstallCheckInputs = [ versionCheckHook ]; - doInstallCheck = true; + desktopItems = lib.optional (!minimal) (makeDesktopItem { + name = "renpy"; + desktopName = "Ren'Py"; + comment = finalAttrs.meta.description; + exec = "renpy-launcher %U"; + icon = "renpy"; + categories = [ "Development" ]; + }); - passthru.updateScript = ./update.sh; + # keep the files in $out/share/renpy/{renpy-dist,lib,renpy.sh} redistributable + dontStrip = true; + dontPatchShebangs = true; + dontPatchELF = true; + postFixup = lib.optionalString (!stdenv.hostPlatform.isDarwin) '' + patchELF $out/share/renpy/renpy + ''; + + nativeInstallCheckInputs = [ versionCheckHook ]; + doInstallCheck = false; # set to true when the version is not unstable + + passthru = { + appver = lib.head (builtins.match "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+).*" finalAttrs.version); + semver = lib.head (builtins.match "([0-9]+\\.[0-9]+\\.[0-9]+).*" finalAttrs.version); + + binSrc = fetchzip { + url = "https://www.renpy.org/dl/${finalAttrs.passthru.semver}/renpy-${finalAttrs.passthru.semver}-sdk.tar.bz2"; + hash = "sha256-wF6Z/lA8CyaCEZg1IqpZ4mG8CF8JgNHBf5KjKIOoKVI="; + }; + + binSrcArm = fetchzip { + url = "https://www.renpy.org/dl/${finalAttrs.passthru.semver}/renpy-${finalAttrs.passthru.semver}-sdkarm.tar.bz2"; + hash = "sha256-DKXghs1XIRrtAGTifMx+7XAbxiqH7qYQiaKhBaO7PBA="; + }; + + distributedRenpy = + if withAarch64LinuxDistributedLibs then + finalAttrs.passthru.binSrcArm + else if withDistributedLibs then + finalAttrs.passthru.binSrc + else + null; + + updateScript = ./update.sh; + }; meta = { description = "Visual Novel Engine"; @@ -138,6 +287,16 @@ stdenv.mkDerivation (finalAttrs: { changelog = "https://renpy.org/doc/html/changelog.html"; license = lib.licenses.mit; platforms = lib.platforms.unix; - maintainers = with lib.maintainers; [ shadowrz ]; + maintainers = with lib.maintainers; [ + shadowrz + ulysseszhan + ]; + sourceProvenance = + with lib.sourceTypes; + [ fromSource ] + ++ lib.optionals (finalAttrs.passthru.distributedRenpy != null) [ + binaryNativeCode # bundled python for windows, linux, and macos in the bin distribution from upstream + binaryBytecode # __pycache__ in the bin distribution from upstream + ]; }; }) diff --git a/pkgs/by-name/re/renpy/shutup-erofs-errors.patch b/pkgs/by-name/re/renpy/shutup-erofs-errors.patch deleted file mode 100644 index 9889c9f8ef85..000000000000 --- a/pkgs/by-name/re/renpy/shutup-erofs-errors.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 09e598ddf1f6af72ccb6c7c9301abff689e64f88 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?=E5=A4=9C=E5=9D=82=E9=9B=85?= - <23130178+ShadowRZ@users.noreply.github.com> -Date: Wed, 17 May 2023 14:32:03 +0800 -Subject: [PATCH] Don't print a backtrace on EROFS - -This can shut up EROFS errors caused by writing to read-only /nix/store. ---- - renpy/script.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/renpy/script.py b/renpy/script.py -index de35457ba..b7d511560 100644 ---- a/renpy/script.py -+++ b/renpy/script.py -@@ -705,6 +705,10 @@ class Script(object): - rpydigest = hashlib.md5(fullf.read()).digest() - - self.write_rpyc_md5(f, rpydigest) -+ except OSError as e: -+ if e.errno != 30: -+ import traceback -+ traceback.print_exc() - except Exception: - import traceback - traceback.print_exc() --- -2.40.1 - diff --git a/pkgs/by-name/re/renpy/steam-preinit-catch.patch b/pkgs/by-name/re/renpy/steam-preinit-catch.patch new file mode 100644 index 000000000000..5b9b13bd761a --- /dev/null +++ b/pkgs/by-name/re/renpy/steam-preinit-catch.patch @@ -0,0 +1,18 @@ +diff --git a/renpy/common/00steam.rpy b/renpy/common/00steam.rpy +index 00581d850..ef00deee7 100644 +--- a/renpy/common/00steam.rpy ++++ b/renpy/common/00steam.rpy +@@ -972,8 +972,11 @@ init -1499 python in achievement: + steam_appid_fn = os.path.join(os.path.dirname(sys.executable), "steam_appid.txt") + + if config.steam_appid is not None: +- with open(steam_appid_fn, "w") as f: +- f.write(str(config.steam_appid) + "\n") ++ try: ++ with open(steam_appid_fn, "w") as f: ++ f.write(str(config.steam_appid) + "\n") ++ except Exception as e: ++ renpy.write_log("Failed to write steam_appid.txt: %r", e) + else: + try: + os.unlink(steam_appid_fn) diff --git a/pkgs/by-name/re/renpy/temp-compile-modules.patch b/pkgs/by-name/re/renpy/temp-compile-modules.patch new file mode 100644 index 000000000000..428db4ab1f44 --- /dev/null +++ b/pkgs/by-name/re/renpy/temp-compile-modules.patch @@ -0,0 +1,36 @@ +diff --git a/renpy/exports/__init__.py b/renpy/exports/__init__.py +index 7265f1b32..dc0d0acc1 100644 +--- a/renpy/exports/__init__.py ++++ b/renpy/exports/__init__.py +@@ -573,6 +573,7 @@ from renpy.exports.scriptexports import ( + load_language, + load_module, + load_string, ++ loaded_modules, + munged_filename, + ) + +diff --git a/renpy/main.py b/renpy/main.py +index 60940a595..ed53e3072 100644 +--- a/renpy/main.py ++++ b/renpy/main.py +@@ -423,6 +423,19 @@ def main(): + print(time.time() - start) + sys.exit(0) + ++ import re ++ pattern = re.compile(r"^(.*)\.rpym$") ++ for fn in renpy.exports.list_files(common=True): ++ m = pattern.match(fn) ++ if m: ++ name = m.group(1) ++ if name in renpy.exports.loaded_modules: ++ print("Module {} already loaded".format(name)) ++ else: ++ print("Compiling module {}".format(name)) ++ renpy.game.script.load_module(name) ++ sys.exit(0) ++ + renpy.game.exception_info = "After loading the script." + + # Find the save directory. diff --git a/pkgs/by-name/re/renpy/update.sh b/pkgs/by-name/re/renpy/update.sh index b09d1fdad4b4..7e71ac65ea80 100755 --- a/pkgs/by-name/re/renpy/update.sh +++ b/pkgs/by-name/re/renpy/update.sh @@ -1,11 +1,28 @@ #!/usr/bin/env nix-shell -#!nix-shell -i bash -p bash nix-update html-xml-utils +#!nix-shell -i bash -p bash nix-update html-xml-utils curl -set -ex +set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" - -codename=`curl -L https://renpy.org/latest.html | hxclean | hxselect -c h1 small` -sed -E -i "s/(version_name = ).*/\1$codename/" $SCRIPT_DIR/package.nix +attr() { + nix-instantiate --eval -A renpy.$1 | tr -d '"' +} +old_version="$(attr version)" nix-update renpy +new_version="$(attr version)" +if [[ "$old_version" == "$new_version" ]]; then + exit 0 +fi + +nix_file="$(attr meta.position | cut -d: -f1)" + +codename="$(curl -L https://renpy.org/latest.html | hxclean | hxselect -c h1 small)" +sed -E -i "s/(version_name = ).*/\1$codename/" "$nix_file" + +old_bin_src_hash="$(attr binSrc.hash)" +new_bin_src_hash="$(nix-hash --type sha256 --to-sri "$(nix-prefetch-url --unpack "$(attr binSrc.url)")")" +sed -i "s|$old_bin_src_hash|$new_bin_src_hash|" "$nix_file" + +old_bin_src_arm_hash="$(attr binSrcArm.hash)" +new_bin_src_arm_hash="$(nix-hash --type sha256 --to-sri "$(nix-prefetch-url --unpack "$(attr binSrcArm.url)")")" +sed -i "s|$old_bin_src_arm_hash|$new_bin_src_arm_hash|" "$nix_file"