(define-module (ruther environment)
#:use-module (guix profiles)
#:use-module (gnu packages)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix build-system trivial)
#:use-module (gnu packages bash)
#:use-module (gnu packages python)
#:use-module (gnu packages guile)
#:export (make-environment
make-interpreted-environment
make-python-environment
make-guile-environment))
;; Makes a package that uses the given environment, and wraps the binaries in it.
;; TODO: replace the paths (only in text files!!! not in binaries...) in all extra-paths files,
;; so instead of symlinking some of the files might have to be just copied and substituted.
;; this also means it might come in handy to create all directories instead and symlink only the files.
(define* (make-environment manifest
#:key
(name "environment")
(binary-paths '("bin"))
(extra-paths '("share/info" "share/man" "share/doc" "share/applications"))
(rename-function #~identity)
(filter-function #~(const #t)))
"Takes a manifest, makes a package that will have all the binaries the profile has, wrapped to use etc/profile
"
(let ((profile (profile (content manifest))))
(package
(name name)
(version "0")
(source #f)
(build-system trivial-build-system)
(inputs
(list bash))
(arguments
(list
#:modules '((guix build utils))
#:builder #~(begin
(use-modules (guix build utils)
(ice-9 format)
(ice-9 ftw))
(mkdir-p #$output)
;; Iterate all binary-paths under profile and make scripts in output
(let* ((profile-path #$profile)
(not-directory? (lambda (base-path)
(lambda (path)
(let ((s (stat (string-append base-path "/" path))))
(not (eq? (stat:type s) 'directory))))))
(binary-folders (list #$@binary-paths))
(profile-binary-folders (map (lambda (folder)
(string-append profile-path "/" folder))
binary-folders))
(output-binary-folders (map (lambda (folder)
(string-append #$output "/" folder))
binary-folders))
(binaries (apply append (map (lambda (folder)
(map
(lambda (file)
(string-append folder "/" file))
(scandir (string-append profile-path "/" folder)
(not-directory? (string-append profile-path "/" folder)))))
binary-folders)))
(profile-binaries (map (lambda (binary)
(string-append profile-path "/" binary))
binaries)))
(define (make-wrapper-script source-file input-file output-file)
(call-with-output-file output-file
(lambda (port)
(format port "!#~a/bin/bash~%"
#$(this-package-input "bash"))
(format port "source ~a~%" source-file)
(format port "exec ~a \"$@\"~%" input-file)))
(chmod output-file #o555))
(for-each mkdir-p output-binary-folders)
(for-each
(lambda (binary)
(make-wrapper-script
(string-append profile-path "/etc/profile")
(string-append profile-path "/" binary)
(string-append #$output "/" (dirname binary) "/" (#$rename-function (basename binary)))))
(filter #$filter-function binaries))
;; Iterate all extra-paths and symlink them to output.
(for-each
(lambda (extra-path)
(when (string-contains extra-path "/")
(mkdir-p (string-append #$output "/" (dirname extra-path))))
(symlink (string-append #$profile "/" extra-path)
(string-append #$output "/" extra-path)))
'#$extra-paths)))))
(synopsis #f)
(description #f)
(home-page #f)
(license #f))))
(define* (make-interpreted-environment
target-name
interpreter-package
interpreter-binary-name
packages-specifications)
(make-environment
(concatenate-manifests
(list (specifications->manifest packages-specifications)
(packages->manifest (list interpreter-package))))
#:name target-name
#:extra-paths '()
#:rename-function
#~(lambda (file)
#$target-name)
#:filter-function
#~(lambda (file)
(string=? (string-append "bin/" #$interpreter-binary-name) file))))
(define* (make-python-environment
target-name
python-packages
#:key (python python))
(make-interpreted-environment
target-name
python
"python3"
python-packages))
(define* (make-guile-environment
target-name
guile-packages
#:key (guile guile))
(make-interpreted-environment
target-name
python
"guile"
guile-packages))