Files
guix-tribes/tribes/packages/source.scm

258 lines
9.9 KiB
Scheme

(define-module (tribes packages source)
#:use-module ((guix licenses) #:prefix license:)
#:use-module (guix download)
#:use-module (guix git-download)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix utils)
#:use-module (gnu packages autotools)
#:use-module (gnu packages base)
#:use-module (gnu packages commencement)
#:use-module (gnu packages gawk)
#:use-module (gnu packages linux)
#:use-module (gnu packages m4)
#:use-module (gnu packages perl)
#:use-module (gnu packages pkg-config)
#:use-module ((tribes packages mix) #:prefix mix:)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:export (fetch-mix-deps
tribes-source-package
tribes-source-directory->local-file))
;; Recursive sha256 of the raw deps tree produced by `mix deps.get --only prod`
;; from the current Tribes mix.lock, with git metadata stripped except for
;; .git/HEAD in SCM dependencies.
(define %tribes-raw-mix-deps-sha256
"0mv4jva8zkx8cq1b84hn65bl913nnhkvf25g6fi93z3jm35jy0pc")
;; Recursive sha256 of the Tribes-specific prepared deps tree, after injecting
;; the upstream secp256k1 source into the Hex package and patching its build
;; recipe to avoid build-time network access.
(define %tribes-mix-deps-sha256
"0ksjnc9gnjijp1nbz3jlvl9kz8w7hx1a0ssms1dvd15rr25gn0d4")
(define %libsecp256k1-upstream-source
(origin
(method git-fetch)
(uri (git-reference
(url "https://github.com/bitcoin-core/secp256k1")
(commit "v0.7.1")))
(file-name (git-file-name "secp256k1" "0.7.1"))
(sha256
(base32 "10cvh8jks3rjg6p7y0vm1v4kw9y7vljbfijj0zxwkxzysxx60w0f"))))
(define %excluded-root-basenames
'(".cache"
".claude"
".direnv"
".elixir_ls"
".git"
".hex"
".home"
".mypy_cache"
".mix-home"
".pre-commit-config.yaml"
"_build"
"deps"
"erl_crash.dump"
"node_modules"
"result"))
(define (root-artifact-path? root file entry)
(let ((entry-path (string-append root "/" entry)))
(or (string=? file entry-path)
(string-prefix? (string-append entry-path "/") file))))
(define (transient-source-file? root file)
(let ((base (basename file)))
(or (any (lambda (entry)
(root-artifact-path? root file entry))
%excluded-root-basenames)
(root-artifact-path? root file ".env")
(string-prefix? ".devenv" base)
(string=? base ".env")
(string-suffix? ".log" base)
(string-suffix? ".tsbuildinfo" base)
(string-suffix? ".db" base)
(string-suffix? ".db-shm" base)
(string-suffix? ".db-wal" base))))
(define (tribes-source-select? root file stat)
(or (string=? file root)
(not (transient-source-file? root file))))
(define (tribes-source-directory->local-file directory)
"Return DIRECTORY as a recursively copied local-file, excluding transient
build artifacts and, when available, anything not tracked in the enclosing Git
checkout."
(let ((git-select? (git-predicate directory)))
(local-file directory
#:recursive? #t
#:select?
(if git-select?
(lambda (file stat)
(and (git-select? file stat)
(tribes-source-select? directory file stat)))
(lambda (file stat)
(tribes-source-select? directory file stat))))))
(define fetch-mix-deps mix:fetch-mix-deps)
(define* (tribes-mix-deps source
#:key
(name "tribes-mix-deps")
(version "0.2.0")
(sha256 %tribes-mix-deps-sha256)
(raw-sha256 %tribes-raw-mix-deps-sha256)
(mix-env "prod")
(mix-target "host"))
"Return the Tribes Mix dependency tree, prepared from the raw lockfile
resolution by injecting extra pre-fetched sources needed for offline builds."
(let ((raw-mix-deps
(fetch-mix-deps source
#:name (string-append name "-raw")
#:version version
#:sha256 raw-sha256
#:mix-env mix-env
#:mix-target mix-target)))
(computed-file
(string-append name "-" version)
(with-imported-modules '((guix build utils))
#~(begin
(use-modules (guix build utils))
(define out #$output)
(define deps-dir (string-append (getcwd) "/deps"))
(copy-recursively #+raw-mix-deps deps-dir #:follow-symlinks? #t)
(invoke #$(file-append coreutils "/bin/chmod") "-R" "u+w" deps-dir)
(let* ((libsecp-dir (string-append deps-dir "/lib_secp256k1"))
(libsecp-src-dir (string-append libsecp-dir "/c_src/secp256k1"))
(libsecp-makefile (string-append libsecp-dir "/Makefile"))
(libsecp-fetched-stamp (string-append libsecp-src-dir "/.fetched")))
(when (file-exists? libsecp-dir)
(mkdir-p (string-append libsecp-dir "/c_src"))
(when (file-exists? libsecp-src-dir)
(delete-file-recursively libsecp-src-dir))
(copy-recursively #+%libsecp256k1-upstream-source
libsecp-src-dir
#:follow-symlinks? #t)
(invoke #$(file-append coreutils "/bin/chmod")
"-R" "u+w"
libsecp-dir)
(when (file-exists? libsecp-makefile)
;; Avoid depending on preserved executable bits or /bin/sh in the
;; generated Autoconf script.
(substitute* libsecp-makefile
(("\\./autogen\\.sh") "sh ./autogen.sh")
(("\\./configure") "sh ./configure")))
(call-with-output-file libsecp-fetched-stamp
(lambda (_port) #t))))
(mkdir-p out)
(copy-recursively deps-dir out #:follow-symlinks? #t)))
#:options
`(#:hash ,(base32 sha256)
#:hash-algo sha256
#:recursive? #t
#:leaked-env-vars ("http_proxy" "https_proxy"
"LC_ALL" "LC_MESSAGES" "LANG" "COLUMNS")))))
(define* (tribes-source-package source
#:key
(mix-deps #f)
(mix-deps-sha256 %tribes-mix-deps-sha256)
(name "tribes")
(version "0.2.0")
(home-page "https://git.teralink.net/tribes/tribes.git")
(synopsis "Tribes social app")
(description
"Tribes social application built from source as a
production Elixir release using a vendored Mix dependency tree."))
"Return a Guix package that builds a production Tribes release from SOURCE,
using MIX-DEPS as the pre-fetched Mix dependency tree resolved from mix.lock."
(let ((mix-deps-source
(or mix-deps
(tribes-mix-deps source
#:name (string-append name "-mix-deps")
#:version version
#:sha256 mix-deps-sha256))))
(mix:mix-release-package
source
#:mix-fod-deps mix-deps-source
#:name name
#:version version
#:home-page home-page
#:synopsis synopsis
#:description description
#:license license:asl2.0
#:native-inputs
(list autoconf
autoconf-wrapper
automake
gcc-toolchain
gawk
grep
gnu-make
libtool
linux-libre-headers
m4
perl
pkg-config
sed)
#:path-inputs
(list autoconf
autoconf-wrapper
automake
gcc-toolchain
gawk
grep
gnu-make
libtool
m4
perl
pkg-config
sed)
#:aclocal-inputs
(list automake libtool)
#:setup-gexp
#~(begin
(define kernel-headers-dir
#$(file-append linux-libre-headers "/include"))
(let ((existing-cpath (getenv "CPATH")))
(setenv "CPATH"
(if existing-cpath
(string-append kernel-headers-dir ":" existing-cpath)
kernel-headers-dir)))
(let ((existing-c-include-path (getenv "C_INCLUDE_PATH")))
(setenv "C_INCLUDE_PATH"
(if existing-c-include-path
(string-append kernel-headers-dir ":"
existing-c-include-path)
kernel-headers-dir)))
(setenv "CC" #$(file-append gcc-toolchain "/bin/gcc"))
(setenv "CXX" #$(file-append gcc-toolchain "/bin/g++"))
(setenv "CPP"
(string-append #$(file-append gcc-toolchain "/bin/gcc")
" -E")))
#:configure-gexp
#~(begin
;; Absinthe has been the last fragile dependency in Guix builds so far.
;; Compile its immediate deps first, then compile Absinthe in its own
;; pass before compiling the remaining dependency graph.
(invoke "mix" "deps.compile" "nimble_parsec" "telemetry" "decimal")
(let ((existing-erl-flags (getenv "ERL_FLAGS")))
;; Keep the scheduler count pinned while compiling Absinthe so
;; toolchain-sensitive ordering issues stay deterministic.
(setenv "ERL_FLAGS"
(if existing-erl-flags
(string-append existing-erl-flags " +S 1:1")
"+S 1:1"))
(invoke "mix" "deps.compile" "absinthe" "--force")
(if existing-erl-flags
(setenv "ERL_FLAGS" existing-erl-flags)
(unsetenv "ERL_FLAGS")))
(invoke "mix" "deps.compile")))))