Subject: Re: [bug#37714] Add renpy package

From: Leo Prikler <[email-masked]>
Date: Mon, 21 Oct 2019 09:35:23 +0200

Am Montag, den 21.10.2019, 09:26 +0200 schrieb Nicolas Goaziou:
> Hello,
> 
> Leo Prikler <[email-masked]> writes:
> 
> > Am Samstag, den 19.10.2019, 22:59 +0200 schrieb Leo Prikler:
> > > - 0003 splits renpy into the python package python2-renpy and the
> > > program itself. This makes the build rules a little easier to
> > > read,
> > > even if they themselves did not change much.
> > Now also with a package description for python2-renpy.
> > > - 0004 fixes the launcher (mostly). One can now set a project
> > > directory, create projects, launch created projects and open
> > > project
> > > directories in the default file explorer. However, setting the
> > > editor
> > > (and hence launching one) still does not work.
> > Adjusted accordingly.
> > - 0005 uses pre-install renpy to compile the games. I've also
> > updated
> > the comments on #:tests? that are inaccurate as of 0002.
> 
> Could you squeeze all those patches into a single one and send it
> again?
> 
> Thank you for all the work so far!
> 
> Regards,
> 
Sure.

Regards,

Leo

From 7acd7d1f6bfbe95f27c5b7bd1752631bccbfd3aa Mon Sep 17 00:00:00 2001
From: Comrade Yuri <yuri@nijino>
Date: Sat, 19 Oct 2019 17:40:27 +0200
Subject: [PATCH] gnu: Add renpy

* gnu/packages/game-development: (python2-pygame-sdl2): New variable.
(python2-renpy): New variable.
(renpy): New variable.
---
 gnu/packages/game-development.scm | 323 ++++++++++++++++++++++++++++++
 1 file changed, 323 insertions(+)

diff --git a/gnu/packages/game-development.scm b/gnu/packages/game-development.scm
index eb92443e0d..10111ebf1c 100644
--- a/gnu/packages/game-development.scm
+++ b/gnu/packages/game-development.scm
@@ -971,6 +971,329 @@ to create fully featured games and multimedia programs in the python language.")
 (define-public python2-pygame
   (package-with-python2 python-pygame))
 
+(define-public python2-pygame-sdl2
+  (let ((real-version "2.1.0")
+        (renpy-version "7.3.5"))
+    (package
+      (inherit python2-pygame)
+      (name "python2-pygame-sdl2")
+      (version (string-append real-version "-for-renpy-" renpy-version))
+      (source
+       (origin
+         (method url-fetch)
+         (uri (string-append "https://www.renpy.org/dl/" renpy-version
+                             "/pygame_sdl2-" version ".tar.gz"))
+         (sha256 (base32 "1bmr7j9mlsc4czpgw70ld15ymyp4wxrk9hdsqad40wjwdxvvg2dr"))))
+      (build-system python-build-system)
+      (arguments
+       `(#:tests? #f                ; tests require pygame to be installed first
+         #:python ,python-2
+         #:phases
+         (modify-phases %standard-phases
+           (add-after 'set-paths 'set-sdl-vars
+             (lambda* (#:key inputs #:allow-other-keys)
+               (setenv "PYGAME_SDL2_CFLAGS"
+                       (string-append "-I"
+                                      (assoc-ref inputs "sdl-union")
+                                      "/include/SDL2 -D_REENTRANT"))
+               (setenv "PYGAME_SDL2_LDFLAGS"
+                       (string-append "-L"
+                                      (assoc-ref inputs "sdl-union")
+                                      "/lib -Wl,-rpath,"
+                                      (assoc-ref inputs "sdl-union")
+                                      "/lib -Wl,--enable-new-dtags -lSDL2"))
+               #t))
+           (add-before 'build 'drop-generated-files
+             (lambda args
+               (delete-file-recursively "gen")
+               (delete-file-recursively "gen3")
+               #t)))))
+      (inputs
+       `(("sdl-union"
+          ,(sdl-union (list sdl2 sdl2-image sdl2-mixer sdl2-ttf)))))
+      (native-inputs
+       `(("python2-cython" ,python2-cython)))
+      (home-page "http://www.renpy.org/")
+      (synopsis "Reimplementation of the Pygame API using SDL2")
+      (description "Pygame_SDL2 reimplements the Pygame API using SDL2,
+staying close to the original, but also adding some SDL2-specific features.
+While it aims to be used as a drop-in replacement, it appears to be
+developed mainly for Ren'py.")
+      (license (list license:lgpl2.1 license:zlib)))))
+
+(define-public python2-renpy
+  (package
+    (name "python2-renpy")
+    (version "7.3.5")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (string-append "https://www.renpy.org/dl/" version
+                           "/renpy-" version "-source.tar.bz2"))
+       (sha256 (base32 "1anr5cfbvbsbik4v4rvrkdkciwhg700k4lydfbs4n85raimz9mw4"))))
+    (build-system python-build-system)
+    (arguments
+     `(#:tests? #f ; Ren'py doesn't seem to package tests
+       #:python ,python-2
+       #:phases
+       (modify-phases %standard-phases
+         (add-after 'unpack 'fix-commands
+           (lambda _
+             (substitute* "renpy/editor.py"
+               (("xdg-open")
+                (which "xdg-open")))
+             #t))
+         (add-after 'set-paths 'set-build-vars
+           (lambda* (#:key inputs #:allow-other-keys)
+             (setenv "RENPY_CYTHON"
+                     (string-append (assoc-ref inputs "python2-cython")
+                                    "/bin/cython"))
+             (setenv "RENPY_DEPS_INSTALL" (string-join (map cdr inputs) ":"))
+             #t))
+         (replace 'build
+           (lambda args
+             (apply
+              (lambda* (build-root #:key inputs outputs #:allow-other-keys)
+                ;; The "module" subdirectory contains a python (really cython)
+                ;; project, which is built using a script, that is thankfully
+                ;; named "setup.py".
+                (chdir "module")
+                (apply (assoc-ref %standard-phases 'build) args)
+                ;; the above causes renpy.__init__ to be compiled but does not
+                ;; compile anything else, hence we do that here
+                (chdir build-root)
+                (delete-file "renpy/__init__.pyc")
+                (invoke "python" "-m" "compileall" "renpy"))
+              (getcwd) args)
+             #t))
+         (replace 'install
+           (lambda args
+             (apply
+              (lambda* (build-root #:key inputs outputs #:allow-other-keys)
+                ;; Again, we have to wrap the module installation.
+                ;; Additionally, we want to install the python code
+                ;; (both source and compiled) in the same directory.
+                (let* ((out (assoc-ref outputs "out"))
+                       (site (string-append "/lib/python"
+                                            ,(version-major+minor
+                                              (package-version python-2))
+                                            "/site-packages")))
+                  (chdir "module")
+                  (apply (assoc-ref %standard-phases 'install) args)
+                  (chdir build-root)
+                  (copy-recursively "renpy"
+                                    (string-append out site "/renpy"))))
+              (getcwd) args)
+             #t)))))
+    (inputs
+     `(("ffmpeg" ,ffmpeg)
+       ("freetype" ,freetype)
+       ("glew" ,glew)
+       ("libpng" ,libpng)
+       ("python2-pygame" ,python2-pygame-sdl2)
+       ("sdl-union"
+        ,(sdl-union (list sdl2 sdl2-image sdl2-mixer sdl2-ttf)))))
+    (native-inputs
+     `(("python2-cython" ,python2-cython)
+       ("xdg-utils" ,xdg-utils)))
+    (home-page "http://www.renpy.org/")
+    (synopsis "Ren'py python module")
+    (description "This package contains the shared libraries and Python modules
+of renpy.")
+    (license license:expat)))
+
+(define-public renpy
+  (package
+    (inherit python2-renpy)
+    (name "renpy")
+    (version "7.3.5")
+    (build-system python-build-system)
+    (arguments
+     `(#:tests? #f ; see python2-renpy
+       #:python ,python-2
+       #:phases
+       (modify-phases %standard-phases
+         (add-after 'unpack 'fix-commands
+           (lambda* (#:key outputs #:allow-other-keys)
+             (substitute* "launcher/game/choose_directory.rpy"
+               (("/usr/bin/python") (which "python2")))
+             (substitute* "launcher/game/front_page.rpy"
+               (("xdg-open")
+                (which "xdg-open")))
+             (substitute* "launcher/game/project.rpy"
+               (("cmd = \\[ executable, \"-EO\", sys.argv\\[0\\] \\]")
+                (string-append "cmd = [ \"" (assoc-ref outputs "out")
+                               "/bin/renpy\" ]"))
+               ;; Projects are still created in the usual style, so we need
+               ;; to adjust the path.
+               (("cmd.append\\(self.path\\)")
+                "cmd.append(self.path + \"/game\")"))
+             #t))
+         (add-after 'unpack 'drop-game-from-paths
+           (lambda _
+             (substitute* (list "launcher/game/gui7.rpy"
+                                "launcher/game/gui7/images.py")
+               ((", \"game\",") ","))
+             #t))
+         (add-before 'build 'start-xserver
+           (lambda* (#:key inputs #:allow-other-keys)
+             (let ((xorg-server (assoc-ref inputs "xorg-server")))
+               (setenv "HOME" (getcwd))
+               (system (format #f "~a/bin/Xvfb :1 &" xorg-server))
+               (setenv "DISPLAY" ":1")
+               #t)))
+         (replace 'build
+           (lambda _
+             (invoke "python" "renpy.py" "launcher" "quit")
+             (invoke "python" "renpy.py" "the_question" "quit")
+             (invoke "python" "renpy.py" "tutorial" "quit")
+             #t))
+         (replace 'install
+           (lambda* (#:key inputs outputs #:allow-other-keys)
+             ;; Here we install our custom renpy program.
+             ;; After finishing this step, "out" will have the following:
+             ;; |-- bin/renpy
+             ;; `-- share/renpy ; i.e. path_to_renpy_base()
+             ;;     `-- common
+             ;;
+             ;; Note that common is also a de facto unused directory in
+             ;; python2-renpy. On other systems, renpy_base would point to
+             ;; site-packages or even somewhere in /opt.
+             ;; The former approach is not as straightforward as it seems
+             ;; -- it causes renpy to load files twice for some weird reason --
+             ;; and the latter is impossible on Guix. Hence the detour through
+             ;; share/renpy and the custom renpy program.
+             ;;
+             ;; As a convention, other games should be installed as
+             ;; subdirectories of share/renpy in their respective outputs as
+             ;; well. This differs from the traditional layout, which is
+             ;; roughly the following:
+             ;; `-- Super Awesome Game
+             ;;     |-- game      ; <- the folder we actually want
+             ;;     |-- lib       ; compiled renpy module and dependencies
+             ;;     |-- renpy     ; Ren'py python code (source + compiled)
+             ;;     |-- Super Awesome Game.py
+             ;;     `-- Super Awesome Game.sh
+             (let* ((out (assoc-ref outputs "out"))
+                    (bin/renpy (string-append out "/bin/renpy")))
+               (mkdir-p (string-append out "/bin"))
+               (copy-recursively "renpy/common"
+                                 (string-append out "/share/renpy/common"))
+               (copy-recursively "gui"
+                                 (string-append out "/share/renpy/gui"))
+
+               (call-with-output-file bin/renpy
+                 (lambda (port)
+                   (format port "#!~a~%" (which "python2"))
+                   (format port "
+from __future__ import print_function
+
+import os
+import sys
+import warnings
+
+def path_to_common(renpy_base):
+    return renpy_base + \"/common\"
+
+def path_to_saves(gamedir, save_directory=None):
+    import renpy  # @UnresolvedImport
+
+    if save_directory is None:
+        save_directory = renpy.config.save_directory
+        save_directory = renpy.exports.fsencode(save_directory)
+
+    if not save_directory:
+        return gamedir + \"/saves\"
+
+    return os.path.expanduser(\"~~/.renpy/\" + save_directory)
+
+def path_to_renpy_base():
+    return \"~a\"
+
+def main():
+    renpy_base = path_to_renpy_base()
+    try:
+        import renpy.bootstrap
+        import renpy.arguments
+    except ImportError:
+        print(\"\"\"Could not import renpy.bootstrap.
+Please ensure you decompressed Ren'Py correctly, preserving the directory
+structure.\"\"\", file=sys.stderr)
+        raise
+
+    args = renpy.arguments.bootstrap()
+    if not args.basedir:
+        print(\"\"\"This Ren'py requires a basedir to launch.
+The basedir is the directory, in which .rpy files live -- usually the 'game'
+subdirectory of a game packaged by Ren'py.
+
+If you want the Ren'py launcher, use renpy-launcher instead.\"\"\",
+              file=sys.stderr)
+        sys.exit()
+
+    renpy.bootstrap.bootstrap(renpy_base)
+
+if __name__ == \"__main__\":
+    main()
+"
+                           (string-append out "/share/renpy"))))
+               (chmod bin/renpy #o755)
+               #t)))
+
+         (add-after 'install 'install-games
+           (lambda* (#:key outputs #:allow-other-keys)
+             (define renpy (assoc-ref outputs "out"))
+             ;; TODO: We should offer a renpy-build-system to make the
+             ;; installation of Ren'py games easier.
+             (define* (install-renpy-game #:key output game name (renpy renpy)
+                                          #:allow-other-keys)
+               (let* ((name (or name (basename game)))
+                      (launcher (string-append output "/bin/renpy-" name))
+                      (share (string-append output "/share/renpy/" name)))
+                 (copy-recursively (string-append game "/game") share)
+                 (mkdir-p (string-append output "/bin"))
+                 (with-output-to-file launcher
+                   (lambda ()
+                     (format #t
+                             "#!~a~%~a ~a \"$@\""
+                             (which "bash")
+                             (string-append renpy "/bin/renpy")
+                             share)))
+                 (chmod launcher #o755)))
+
+             (install-renpy-game #:output (assoc-ref outputs "out")
+                                 #:game "launcher")
+
+             (install-renpy-game #:output (assoc-ref outputs "the-question")
+                                 #:game "the_question"
+                                 #:name "the-question")
+
+             (install-renpy-game #:output (assoc-ref outputs "tutorial")
+                                 #:game "tutorial")
+             #t))
+         (replace 'wrap
+           (lambda* (#:key inputs outputs #:allow-other-keys)
+             (wrap-program (string-append (assoc-ref outputs "out")
+                                          "/bin/renpy")
+               `("PYTHONPATH" = (,(getenv "PYTHONPATH"))))
+             #t)))))
+    (inputs
+     `(("python2-tkinter" ,python-2 "tk")
+       ("python2-pygame" ,python2-pygame-sdl2)
+       ("python2-renpy" ,python2-renpy)
+       ("xorg-server" ,xorg-server)))
+    (outputs
+     (list "out" "tutorial" "the-question"))
+    (home-page "http://www.renpy.org/")
+    (synopsis "Visual Novel Engine")
+    (description "Ren'Py is a visual novel engine -- used by thousands of
+creators from around the world -- that helps you use words, images, and sounds
+to tell interactive stories that run on computers and mobile devices.  These
+can be both visual novels and life simulation games.  The easy to learn script
+language allows anyone to efficiently write large visual novels, while its
+Python scripting is enough for complex simulation games.")
+    (license license:expat)))
+
 (define-public grafx2
   (package
     (name "grafx2")
-- 
2.23.0