From 21d03fb536e7f2193cccf9b58d4fc8aac2fd91cb Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Thu, 5 Dec 2024 00:22:24 +0000 Subject: bin: Add --output-directory argument. * bin/ravanan: Import (ravanan work utils). (%options): Add --output-directory. (print-usage): Document it. (symlink-to-output-directory): New function. (main): Symlink to output directory if it is specified. --- bin/ravanan | 163 ++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/bin/ravanan b/bin/ravanan index bf27fe9..f47e7b9 100755 --- a/bin/ravanan +++ b/bin/ravanan @@ -33,7 +33,8 @@ exec guile --no-auto-compile -e main -s "$0" "$@" (ravanan config) (ravanan reader) (ravanan utils) - (ravanan workflow)) + (ravanan workflow) + (ravanan work utils)) (define %options (list (option (list "batch-system" "batchSystem") #t #f @@ -70,6 +71,12 @@ exec guile --no-auto-compile -e main -s "$0" "$@" (lambda (opt name arg result) (acons 'slurm-nice (string->number arg) result))) + ;; The shorter outdir form is to maintain compatibility with cwltool and + ;; cwltest. + (option (list "output-directory" "outdir") #t #f + (lambda (opt name arg result) + (acons 'outdir arg + result))) (option (list "help") #f #t (lambda (opt name arg result) (acons 'help #t result))) @@ -85,6 +92,8 @@ exec guile --no-auto-compile -e main -s "$0" "$@" "Usage: ~a [OPTIONS] CWL-WORKFLOW INPUTS Run CWL-WORKFLOW with INPUTS. + --output-directory=DIRECTORY + --outdir=DIRECTORY output directory --version print version and exit --batch-system=BATCH-SYSTEM batch system to run jobs on; @@ -119,6 +128,46 @@ files that have the token in the @verbatim{SLURM_JWT=token} format." string-trim-both get-string-all))) +(define (symlink-to-output-directory store output-directory tree) + "Symlink @code{File} or @code{Directory} type objects in JSON @var{tree} from the +@var{store} into @var{output-directory}. Return @var{tree} with updated +@code{path} properties." + (cond + ;; Array + ((vector? tree) + (vector-map (cut symlink-to-output-directory store output-directory <>) + tree)) + ;; Object + ((list? tree) + ;; We cannot use object-type to determine the type since it would error out + ;; when it cannot determine the type. + (if (member (assoc-ref tree "class") + (list "File" "Directory")) + ;; If File or Directory, symlink it to output directory, and update + ;; path. + (let ((output-directory-path + (match (file-name-split + (relative-file-name (assoc-ref tree "path") + (expand-file-name %store-files-directory + store))) + ((_ parts ...) + (expand-file-name (file-name-join parts) + output-directory))))) + ;; Symlink file or directory to output directory. + (symlink (assoc-ref tree "path") + output-directory-path) + ;; Update path. + (assoc-set tree + (cons "path" output-directory-path))) + ;; Else, recurse. + (map (match-lambda + ((key . value) + (cons key + (symlink-to-output-directory store output-directory value)))) + tree))) + ;; Atom + (else tree))) + (define main (match-lambda ((program args ...) @@ -155,57 +204,63 @@ files that have the token in the @verbatim{SLURM_JWT=token} format." ((workflow-file inputs-file) ;; We must not try to compile guix manifest files. (set! %load-should-auto-compile #f) - (scm->json (guard (c ((manifest-file-error? c) - ;; Steps may provide their own - ;; SoftwareRequirement. So, at this point, we do - ;; not know if a manifest file is required and - ;; can't check for these manifest file errors - ;; right away. Instead, we depend on exceptions - ;; bubbled up from lower down the stack. - (let ((file (manifest-file-error-file c))) - (cond - ((not file) - (error "--guix-manifest not specified")) - ((not (file-exists? file)) - (error "Manifest file ~a does not exist" - file)) - (else - (error "Error loading manifest file" - file) - (raise-exception c)))))) - (run-workflow (file-name-stem workflow-file) - (and (assq 'guix-manifest-file args) - (canonicalize-path - (assq-ref args 'guix-manifest-file))) - (and (assq-ref args 'guix-channels-file) - (load-script - (canonicalize-path - (assq-ref args 'guix-channels-file)) - #:modules '((guile) - (guix channels)))) - (read-workflow workflow-file) - (read-inputs inputs-file) - (case (assq-ref args 'batch-system) - ((single-machine) - (or (assq-ref args 'scratch) - (getcwd))) - ((slurm-api) - (assq-ref args 'scratch))) - ;; FIXME: This is a bit of a hack to - ;; avoid canonizing remote paths. - (if (file-name-absolute? (assq-ref args 'store)) - (assq-ref args 'store) - (canonicalize-path (assq-ref args 'store))) - (case (assq-ref args 'batch-system) - ((single-machine) 'single-machine) - ((slurm-api) - (slurm-api-batch-system - (assq-ref args 'slurm-api-endpoint) - (and (assq-ref args 'slurm-jwt) - (read-jwt (assq-ref args 'slurm-jwt))) - (assq-ref args 'slurm-partition) - (assq-ref args 'slurm-nice)))) - #:guix-daemon-socket (assq-ref args 'guix-daemon-socket))) - (current-output-port) - #:pretty #t) + (let* ( ;; FIXME: This is a bit of a hack to avoid canonizing remote + ;; paths. + (store (if (file-name-absolute? (assq-ref args 'store)) + (assq-ref args 'store) + (canonicalize-path (assq-ref args 'store)))) + (outputs (guard (c ((manifest-file-error? c) + ;; Steps may provide their own + ;; SoftwareRequirement. So, at this point, we do + ;; not know if a manifest file is required and + ;; can't check for these manifest file errors + ;; right away. Instead, we depend on exceptions + ;; bubbled up from lower down the stack. + (let ((file (manifest-file-error-file c))) + (cond + ((not file) + (error "--guix-manifest not specified")) + ((not (file-exists? file)) + (error "Manifest file ~a does not exist" + file)) + (else + (error "Error loading manifest file" + file) + (raise-exception c)))))) + (run-workflow (file-name-stem workflow-file) + (and (assq 'guix-manifest-file args) + (canonicalize-path + (assq-ref args 'guix-manifest-file))) + (and (assq-ref args 'guix-channels-file) + (load-script + (canonicalize-path + (assq-ref args 'guix-channels-file)) + #:modules '((guile) + (guix channels)))) + (read-workflow workflow-file) + (read-inputs inputs-file) + (case (assq-ref args 'batch-system) + ((single-machine) + (or (assq-ref args 'scratch) + (getcwd))) + ((slurm-api) + (assq-ref args 'scratch))) + store + (case (assq-ref args 'batch-system) + ((single-machine) 'single-machine) + ((slurm-api) + (slurm-api-batch-system + (assq-ref args 'slurm-api-endpoint) + (and (assq-ref args 'slurm-jwt) + (read-jwt (assq-ref args 'slurm-jwt))) + (assq-ref args 'slurm-partition) + (assq-ref args 'slurm-nice)))) + #:guix-daemon-socket (assq-ref args 'guix-daemon-socket))))) + (scm->json (if (assq-ref args 'outdir) + (symlink-to-output-directory store + (assq-ref args 'outdir) + outputs) + outputs) + (current-output-port) + #:pretty #t)) (newline (current-output-port)))))))) -- cgit v1.2.3