This hasn't been used in a long time (Guix uses its own CI system), and it probably doesn't work anymore.
(cherry picked from commit 23c9ca3e94669087d463642baea0cf35a0b8d72f)
QGLBQ5IOVSMBQRXXHO4LOJG4A7V3HSUZESCP2N235A7L4VVOBKCQC
SVDJSA6U3T3WMJHXZ24IB7HJYDN2XG35DWGDS2FV6CR2Y3YKRFEAC
UL6TSQEAQ36LH5U4FTXROJYVPG3BFZZ2H6XUZRK2HJRZA545A5YAC
3K3GDTUQ3MGTU6F5MTT5YVTDMNTSJIU5K2T344DMR4Y53PJQC6FQC
YHDX3LB65VVAVCLK7BKQCSWRT54Q3RWTPFMFHTUSCQD7D7VCJ2KQC
3HVZCXJR6DHPOGJ4YCGHQUNHTPPC6D2QEFRLKRJIISAYATKJMRIQC
UXRNODRJJU7A33F4PG24WOHKTJFI3XQMZGT2MUOBRAH2MPQ7NZ2AC
ICYJNJWHY6KBNLM2EMSW7VJ35OIOHN26JO3FG32VYTKNLFNZAREQC
STZE4KKRL2AGCPP2FOMBNZHJTRTKJQZSEKALWEV6FK5BII6H7EDAC
HP5WJLQUNP7PXGVBYOMWRCZ4QKDECULS2EAFX2FPHRUXV2CMMYCAC
GBCPEGYCLFC5FUNAK2BV47G3EGQ26TPXSFCKVCY23QYET7A5A5YAC
Y6H7Y3OTXVLF6PJ5BR6WXS7KVS4VMTUKRYVM6FALKMY3DCBYEJSQC
KNLKTCDMQNYJ2Y4B7PDQ4WLZUSYKU4VV74YDCDJH5OBCYGP7W4VQC
2KJFOYGSVN3TYHG7YCXJLNYMHCVY7E6LYV4QPRTUSJLJCRGKF7HAC
FV2M6MOTAP4BJMEKU5XUDVEACWEJGEIRCCE2MRY3F6SF2SFOE3MQC
M3A5PZIHA3LDVTBTKGTLCYGJSMNJOFXNO5GBPAUDHO5XGHLKYYPQC
QMW24O5S43MYF5ZTBULUEZNDW2FKJ3QP2GL46P6B2SVGSQLVEACAC
FAIJDQKZH6SUUNCKW5RIOZ37YRAO5ACS6FX76MHRNN53LDBWO6ZQC
NNQ7IQJXKU6EBW45T3EHYDLC7GY5CYSZJKC3IGOWNUW22MYD5ORQC
T4LLYESZ2HUXSLKZ6GNBLVWUVG7R5IDFHYHYO773QIZ6QTOOXR2AC
ODNCGFQ5FPKFI624BVMLW7PJ2EFJOR3TY66OCZM42UNNTWBCF2TQC
3HZY24CX4U2TO74HOY4YX3LBJIYF4DLXHCIY7J2RASAC4COMSMZAC
LZVO64YG43JD7YMZSCTZNOBS5ROZA4FMPKJW2YOMHX2V5PTGBVWQC
CQTN62OHT4DY35E2MJEG7GFTVNEE5KRDMV6ASBQLBHN7BUDK7WHAC
D5QIOJGPKQJIYBUCSC3MFJ3TXLPNZ2XMI37GXMFRVRFWWR2VMTFAC
VML3XVFYL57QICENFGVPB4SMHUQX2IOJVDZIDRK5KGS657SKQVWAC
W2VB36NMID2UYEJB5THOAYYNJEQ42WIF2K7IRXEUYMNTI2AHFYTAC
HXS5NUHWFIQSEX6HEKVOBX62IZV5OV4HFTVDRMMQBNM4GXMF4WKQC
IS7GUIWYGOHEOYMOULIRE45MG7FFSBDN2MYDPKLGZ6PIQ5X4F5XQC
DBPIYHMAHIV2ENFEWE474K5LV27WAHMR3ZZ5HF6FM7YVDM3MK4HQC
VU2OLHD246DFWL2WPFSKWPLXTN3WRL25TSRU4PJMJBNWDF35OFRAC
N22GPKYTOLZLBGTGDATQDVZ4R5APZEAOIA7L32X4UXBH4XNI7MWAC
FDE3BJAPDEP3BYT5A5GEGLNXPPZLA2KTGXB4ZNYRP4LJ7IFRKYXAC
5SHCWE7XPQORSOLY7HGAIK2ODKBFPY4KVXRL2W7X6D4WALU544HQC
MPFSVI5XLGXVPJP5OJXMT7CF2SFFFFT6R36O74MJB5TF6474E54AC
IMQRX4MPANNOXDP6THT2FA4576CDFY44LGNCS74BHV6DWLOCXQOQC
NS7SND6RF6X3CVSVP7MLAQQZFA64E63YKFFABPEAGEPHX6PNWBOQC
RFE6T5LGBFFNEPHZOPF4UNMFC2L4CGD5TPAMOXDLRPH3TZJ43UBAC
# When the expression is in a .scm file, assume it's a Guile + Guix
# build expression.
my $exprType =
$c->stash->{params}->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";
#!/bin/sh
# Aside from this initial boilerplate, this is actually -*- scheme -*- code.
main="(module-ref (resolve-interface '(hydra-eval-guile-jobs)) 'eval-guile-jobs)"
# Keep the host's GUILE_LOAD_PATH unchanged to allow the installed Guix to
# be used. This moves Guix modules possibly out of control, but solves
# bootstrapping issues.
#
# Use `--fresh-auto-compile' to ignore any available .go, and force
# recompilation. This is because checkouts in the store has mtime set to
# the epoch, and thus .go files look newer, even though they may not
# correspond.
exec ${GUILE:-@GUILE@} --no-auto-compile --fresh-auto-compile \
-l "$0" -c "(apply $main (cdr (command-line)))" "$@"
!#
;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of Hydra.
;;;
;;; Hydra is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; Hydra is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with Hydra. If not, see <http://www.gnu.org/licenses/>.
(define-module (hydra-eval-guile-jobs)
#:use-module (sxml simple)
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:export (job-evaluations->xml
eval-guile-jobs))
(define (guix-variable module name)
"Dynamically link variable NAME under Guix module MODULE and return it.
Note: this is used instead of `@', because when using `@' in an uncompiled
file, Guile tries to load the module directly as it reads the source, which
fails in our case, leading to the creation of empty (guix ...) modules."
;; TODO: fail with an XML error description
(let ((m (resolve-interface `(guix ,module))))
(module-ref m name)))
(define (%derivation-system drv)
;; XXX: Awful hack to workaround the fact that `derivation-system', which
;; is a macro, cannot be referred to dynamically.
(struct-ref drv 3))
(define strip-store-path
(let* ((store (or (getenv "NIX_STORE_DIR") "/nix/store"))
(store-path-rx
(make-regexp (string-append "^.*" (regexp-quote store)
"/[^-]+-(.+)$"))))
(lambda (path)
(or (and=> (regexp-exec store-path-rx path)
(lambda (match)
(let ((path (match:substring match 1)))
path)))
path))))
(define (derivation-path->name drv)
"Return the base name of DRV, sans hash and `.drv' extension."
(let ((d (strip-store-path drv)))
(if (string-suffix? ".drv" d)
(string-drop-right d 4)
d)))
(define (register-gc-root drv roots-dir)
"Register a permanent garbage collector root under ROOTS-DIR for DRV."
(let ((root (string-append roots-dir "/" (basename drv))))
(unless (file-exists? root)
(symlink drv root))))
(define* (job-evaluations->sxml jobs
#:key gc-roots-dir)
"Return the hydra-eval-jobs SXML form for the result of JOBS, a list of
symbol/thunk pairs."
`(*TOP*
(*PI* xml "version='1.0' encoding='utf-8'")
"\n"
(jobs "\n"
,@(map (match-lambda
(((? symbol? name) . (? thunk? thunk))
(let* ((result (save-module-excursion
(lambda ()
(set-current-module %user-module)
(with-output-to-port (%make-void-port "w")
thunk))))
(drv (assoc-ref result 'derivation)))
(define (opt-attr xml-name name)
(match (assoc name result)
((_ . value)
`((,xml-name ,value)))
(_
'())))
;; XXX: Add <arg ...> tags?
`(job (@ (jobName ,name)
(drvPath ,drv)
,@(opt-attr 'homepage 'home-page)
(license
,(let loop ((license (assoc-ref result 'license)))
(match license
((? struct?)
(struct-ref license 0))
((l ...)
(string-join (map loop l)))
(_ ""))))
,@(opt-attr 'description 'description)
(maintainers
,(string-join (or (assoc-ref result 'maintainers)
'())
", "))
(maxSilent
,(number->string (or (assoc-ref result
'max-silent-time)
3600)))
(timeout
,(number->string (or (assoc-ref result 'timeout)
72000)))
(nixName ,(derivation-path->name drv))
(schedulingPriority
,(number->string (or (assoc-ref result
'scheduling-priority)
10)))
(system
,(call-with-input-file drv
(compose %derivation-system
(guix-variable 'derivations
'read-derivation)))))
"\n"))))
jobs))))
(define* (job-evaluations->xml jobs port
#:key gc-roots-dir)
(set-port-encoding! port "UTF-8")
(sxml->xml (job-evaluations->sxml jobs #:gc-roots-dir gc-roots-dir)
port))
;;;
;;; Command-line entry point.
;;;
(define (parse-arguments args)
"Traverse ARGS, a list of command-line arguments compatible with
`hydra-eval-jobs', and return the name of the file that defines the jobs, an
expression that returns the entry point in that file (a unary procedure), the
list of name/value pairs passed to that entry point, as well as a GC root
directory or #f."
(define (module-directory dir)
(let ((d (string-append dir "/share/guile/site/2.0")))
(if (file-exists? d)
d
dir)))
(let loop ((args args)
(result '())
(file #f)
(entry 'hydra-jobs)
(roots-dir #f))
(match args
(()
(if (not file)
(error "hydra-eval-guile-jobs: no expression file given")
(values file entry (reverse result) roots-dir)))
(("-I" name=dir rest ...)
(let* ((dir (match (string-tokenize name=dir
(char-set-complement (char-set
#\=)))
((_ dir) dir)
((dir) dir)))
(dir* (module-directory dir)))
(format (current-error-port) "adding `~a' to the load path~%" dir*)
(set! %load-path (cons dir* %load-path))
(set! %load-compiled-path (cons dir* %load-compiled-path)))
(loop rest result file entry roots-dir))
(("--argstr" name value rest ...)
(loop rest (alist-cons (string->symbol name) value result)
file entry roots-dir))
(("--arg" name expr rest ...)
(let ((value (eval (call-with-input-string expr read)
(current-module))))
(loop rest (alist-cons (string->symbol name) value result)
file entry roots-dir)))
(("--gc-roots-dir" dir rest ...)
(loop rest result file entry dir))
(("-j" _ rest ...) ; XXX: what's this?
(loop rest result file entry roots-dir))
(("--entry" expr rest ...) ; entry point, like `guile -e'
(let ((expr (call-with-input-string expr read)))
(loop rest result file expr roots-dir)))
((file rest ...) ; source file that defines the jobs
(loop rest result file entry roots-dir))
(_
(error "hydra-eval-guile-jobs: invalid arguments" args)))))
(define %user-module
;; Hydra user module.
;; TODO: Make it a sandbox.
(let ((m (make-module)))
(beautify-user-module! m)
m))
(define (eval-guile-jobs . args)
(setlocale LC_ALL "")
(let-values (((file entry args gc-roots-dir)
(parse-arguments args)))
(save-module-excursion
(lambda ()
(set-current-module %user-module)
;; The standard output must contain only XML.
(with-output-to-port (%make-void-port "w")
(lambda ()
(primitive-load file)))))
(let* ((entry (eval entry %user-module))
(store ((guix-variable 'store 'open-connection)))
(jobs (entry store args)))
(unless (string? gc-roots-dir)
(format (current-error-port)
"warning: --gc-roots-dir not specified~%"))
(job-evaluations->xml jobs (current-output-port)
#:gc-roots-dir gc-roots-dir))))
;; Resolve Guix modules lazily.
,(map (match-lambda
((name . path)
`(output (@ (name ,name) (path ,path)))))
((guix-variable 'derivations
'derivation-path->output-paths)
drv))
(when gc-roots-dir
;; Register DRV as a GC root so that it's not collected by
;; the time 'hydra-queue-runner' attempts to build it.
(register-gc-root drv gc-roots-dir))
my ($exprType, $input) = @_;
my $result;
if ($exprType eq "guile") {
$result = "'((file-name . \"" . ${input}->{storePath} . "\")" .
(defined $input->{revision} ? "(revision . \"" . $input->{revision} . "\")" : "") .
(defined $input->{revCount} ? "(revision-count . " . $input->{revCount} . ")" : "") .
(defined $input->{gitTag} ? "(git-tag . \"" . $input->{gitTag} . "\")" : "") .
(defined $input->{shortRev} ? "(short-revision . \"" . $input->{shortRev} . "\")" : "") .
(defined $input->{version} ? "(version . \"" . $input->{version} . "\")" : "") .
")";
} else {
$result = "{ outPath = builtins.storePath " . $input->{storePath} . "" .
"; inputType = \"" . $input->{type} . "\"" .
(defined $input->{uri} ? "; uri = \"" . $input->{uri} . "\"" : "") .
(defined $input->{revNumber} ? "; rev = " . $input->{revNumber} . "" : "") .
(defined $input->{revision} ? "; rev = \"" . $input->{revision} . "\"" : "") .
(defined $input->{revCount} ? "; revCount = " . $input->{revCount} . "" : "") .
(defined $input->{gitTag} ? "; gitTag = \"" . $input->{gitTag} . "\"" : "") .
(defined $input->{shortRev} ? "; shortRev = \"" . $input->{shortRev} . "\"" : "") .
(defined $input->{version} ? "; version = \"" . $input->{version} . "\"" : "") .
(defined $input->{outputName} ? "; outputName = \"" . $input->{outputName} . "\"" : "") .
(defined $input->{drvPath} ? "; drvPath = builtins.storePath " . $input->{drvPath} . "" : "") .
";}";
}
return $result;
my ($input) = @_;
return
"{ outPath = builtins.storePath " . $input->{storePath} . "" .
"; inputType = \"" . $input->{type} . "\"" .
(defined $input->{uri} ? "; uri = \"" . $input->{uri} . "\"" : "") .
(defined $input->{revNumber} ? "; rev = " . $input->{revNumber} . "" : "") .
(defined $input->{revision} ? "; rev = \"" . $input->{revision} . "\"" : "") .
(defined $input->{revCount} ? "; revCount = " . $input->{revCount} . "" : "") .
(defined $input->{gitTag} ? "; gitTag = \"" . $input->{gitTag} . "\"" : "") .
(defined $input->{shortRev} ? "; shortRev = \"" . $input->{shortRev} . "\"" : "") .
(defined $input->{version} ? "; version = \"" . $input->{version} . "\"" : "") .
(defined $input->{outputName} ? "; outputName = \"" . $input->{outputName} . "\"" : "") .
(defined $input->{drvPath} ? "; drvPath = builtins.storePath " . $input->{drvPath} . "" : "") .
";}";
my ($jobs, $nixExprInput) = evalJobs($inputInfo, $exprType, $jobset->nixexprinput, $jobset->nixexprpath);
my ($jobs, $nixExprInput) = evalJobs($inputInfo, $jobset->nixexprinput, $jobset->nixexprpath);