about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.guix/e2e-tests.scm6
-rw-r--r--HACKING.md2
-rw-r--r--e2e-tests/jobs/command-line-tool-with-array-input.yaml3
-rw-r--r--e2e-tests/jobs/unseparated-prefix-arguments.yaml1
-rw-r--r--e2e-tests/tests.yaml18
-rw-r--r--e2e-tests/tools/command-line-tool-with-array-input.scm3
-rw-r--r--e2e-tests/tools/unseparated-prefix-arguments.scm3
-rw-r--r--ravanan/reader.scm18
-rw-r--r--ravanan/work/command-line-tool.scm85
-rw-r--r--ravanan/workflow.scm15
10 files changed, 104 insertions, 50 deletions
diff --git a/.guix/e2e-tests.scm b/.guix/e2e-tests.scm
index 4dfc8b0..201d482 100644
--- a/.guix/e2e-tests.scm
+++ b/.guix/e2e-tests.scm
@@ -26,8 +26,8 @@
   #:use-module (ice-9 match))
 
 (define-public ccwl
-  (let ((commit "5f6e0d93bb08446d2c6f99484523697a2bda7b4d")
-        (revision "1"))
+  (let ((commit "4f757fc0a4d4af067e0d22ad4020c1b18cc3fd24")
+        (revision "2"))
     (package
       (inherit guix:ccwl)
       (name "ccwl")
@@ -40,7 +40,7 @@
                 (file-name (git-file-name name version))
                 (sha256
                  (base32
-                  "19hwkgj8gsfw24fs682b4wxs5chnwj6hv0rvs23vmq7309mgf242"))))
+                  "0jch7pr85r2cd9g4b4gq748mbnbi5jg0dqyp8ixhhwac2yqbmyqh"))))
       (native-inputs
        (modify-inputs (package-native-inputs guix:ccwl)
          (prepend guile-run64))))))
diff --git a/HACKING.md b/HACKING.md
index 4d8b6fe..7df6d02 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -18,8 +18,6 @@ $(guix build -L ../.guix -f ../.guix/e2e-tests.scm)
 ```
 Since ravanan depends on guix, and that guix may be too old, you may need to run this command outside the usual development environment.
 
-## Run specific end-to-end test
-
 When hacking on ravanan, you may be trying to get a specific test to pass, and may want to repeatedly run that specific test alone. You can do this by passing additional cwltest arguments. For example, to only run the `hello-world` test:
 ```
 $(guix build -L ../.guix -f ../.guix/e2e-tests.scm) -s hello-world
diff --git a/e2e-tests/jobs/command-line-tool-with-array-input.yaml b/e2e-tests/jobs/command-line-tool-with-array-input.yaml
new file mode 100644
index 0000000..a0fc50c
--- /dev/null
+++ b/e2e-tests/jobs/command-line-tool-with-array-input.yaml
@@ -0,0 +1,3 @@
+messages:
+  - foo
+  - bar
diff --git a/e2e-tests/jobs/unseparated-prefix-arguments.yaml b/e2e-tests/jobs/unseparated-prefix-arguments.yaml
new file mode 100644
index 0000000..e179c1b
--- /dev/null
+++ b/e2e-tests/jobs/unseparated-prefix-arguments.yaml
@@ -0,0 +1 @@
+message: foo
diff --git a/e2e-tests/tests.yaml b/e2e-tests/tests.yaml
index d14b93a..70f90f1 100644
--- a/e2e-tests/tests.yaml
+++ b/e2e-tests/tests.yaml
@@ -241,3 +241,21 @@
       class: File
       size: 13
       checksum: sha1$47a013e660d408619d894b20806b1d5086aab03b
+- id: command-line-tool-with-array-input
+  doc: CommandLineTool with array input
+  tool: tools/command-line-tool-with-array-input.cwl
+  job: jobs/command-line-tool-with-array-input.yaml
+  output:
+    output_message:
+      class: File
+      size: 8
+      checksum: sha1$d53a205a336e07cf9eac45471b3870f9489288ec
+- id: unseparated-prefix-arguments
+  doc: Prefix arguments with separate as false
+  tool: tools/unseparated-prefix-arguments.cwl
+  job: jobs/unseparated-prefix-arguments.yaml
+  output:
+    output_message:
+      class: File
+      size: 12
+      checksum: sha1$598114d347c67f3f6142f60a4b5afcb3482f27a8
diff --git a/e2e-tests/tools/command-line-tool-with-array-input.scm b/e2e-tests/tools/command-line-tool-with-array-input.scm
new file mode 100644
index 0000000..38a8722
--- /dev/null
+++ b/e2e-tests/tools/command-line-tool-with-array-input.scm
@@ -0,0 +1,3 @@
+(command #:inputs (messages #:type (array string))
+         #:run "echo" messages
+         #:outputs (output_message #:type stdout))
diff --git a/e2e-tests/tools/unseparated-prefix-arguments.scm b/e2e-tests/tools/unseparated-prefix-arguments.scm
new file mode 100644
index 0000000..03f68e2
--- /dev/null
+++ b/e2e-tests/tools/unseparated-prefix-arguments.scm
@@ -0,0 +1,3 @@
+(command #:inputs (message #:type string)
+         #:run "echo" ("-a" message #:separate? #f) ("-b" "bar" #:separate? #f)
+         #:outputs (output_message #:type stdout))
diff --git a/ravanan/reader.scm b/ravanan/reader.scm
index bb825ed..35545dd 100644
--- a/ravanan/reader.scm
+++ b/ravanan/reader.scm
@@ -156,6 +156,17 @@ the @code{required} field when it is not specified."
     (vector `(("pattern" . ,secondary-files)
               ("required" . ,default-required))))))
 
+(define (normalize-command-line-binding binding)
+  "Normalize @code{CommandLineBinding} @var{binding}."
+  (assoc-set binding
+    (cons "separate"
+          (cond
+           ((assoc "separate" binding)
+            => (match-lambda
+                 ((_ . separate)
+                  (coerce-type separate 'boolean))))
+           (else #t)))))
+
 (define (normalize-formal-input input)
   "Normalize formal @var{input}."
   (maybe-assoc-set input
@@ -167,7 +178,10 @@ the @code{required} field when it is not specified."
     (cons "secondaryFiles"
           (maybe-bind (maybe-assoc-ref (just input) "secondaryFiles")
                       (compose just
-                               (cut normalize-secondary-files <> #t))))))
+                               (cut normalize-secondary-files <> #t))))
+    (cons "inputBinding"
+          (maybe-bind (maybe-assoc-ref (just input) "inputBinding")
+                      (compose just normalize-command-line-binding)))))
 
 (define (normalize-formal-output output)
   "Normalize formal @var{output}."
@@ -192,7 +206,7 @@ the @code{required} field when it is not specified."
                          ((string? argument)
                           `(("valueFrom" . ,argument)))
                          ((list? argument)
-                          argument)
+                          (normalize-command-line-binding argument))
                          (else
                           (error "Invalid argument" argument))))
                       arguments))))
diff --git a/ravanan/work/command-line-tool.scm b/ravanan/work/command-line-tool.scm
index 175a561..cb945f9 100644
--- a/ravanan/work/command-line-tool.scm
+++ b/ravanan/work/command-line-tool.scm
@@ -296,12 +296,13 @@ condition on unsupported URI schemes."
       json->scm)))
 
 (define-immutable-record-type <command-line-binding>
-  (command-line-binding position prefix type value item-separator)
+  (command-line-binding position prefix type value separate? item-separator)
   command-line-binding?
   (position command-line-binding-position)
   (prefix command-line-binding-prefix)
   (type command-line-binding-type)
   (value command-line-binding-value)
+  (separate? command-line-binding-separate?)
   (item-separator command-line-binding-item-separator))
 
 (define (command-line-binding->args binding)
@@ -332,17 +333,20 @@ the G-expressions are inserted."
                       (just (list (string-join args item-separator))))
                     args))))))
      (else
-      (append (maybe->list prefix)
-              (list (case type
-                      ((string)
-                       value)
-                      ((int float)
-                       (number->string value))
-                      ((File)
-                       (assoc-ref* value "path"))
-                      (else
-                       (user-error "Invalid formal input type ~a"
-                                   type)))))))))
+      (let ((args (append (maybe->list prefix)
+                          (list (case type
+                                  ((string)
+                                   value)
+                                  ((int float)
+                                   (number->string value))
+                                  ((File)
+                                   (assoc-ref* value "path"))
+                                  (else
+                                   (user-error "Invalid formal input type ~a"
+                                               type)))))))
+        (if (command-line-binding-separate? binding)
+            args
+            (list (string-join args ""))))))))
 
 (define (build-command base-command arguments formal-inputs inputs)
   "Return a list of @code{<command-line-binding>} objects for a
@@ -351,7 +355,7 @@ the G-expressions are inserted."
 @code{<command-line-binding>} objects may be strings or G-expressions. The
 G-expressions may reference @var{inputs} and @var{runtime} variables that must
 be defined in the context in which the G-expressions are inserted."
-  (define (value->command-line-binding position prefix value)
+  (define (value->command-line-binding position prefix value separate?)
     (let ((type (object-type value)))
       (cond
        ((cwl-array-type? type)
@@ -361,11 +365,13 @@ be defined in the context in which the G-expressions are inserted."
                               (vector-map (cut value->command-line-binding
                                                %nothing
                                                %nothing
-                                               <>)
+                                               <>
+                                               #f)
                                           value)
+                              separate?
                               %nothing))
        (else
-        (command-line-binding position prefix type value %nothing)))))
+        (command-line-binding position prefix type value separate? %nothing)))))
 
   (define (argument->command-line-binding i argument)
     (value->command-line-binding (cond
@@ -373,11 +379,12 @@ be defined in the context in which the G-expressions are inserted."
                                    => string->number)
                                   (else i))
                                  (maybe-assoc-ref (just argument) "prefix")
-                                 (assoc-ref* argument "valueFrom")))
+                                 (assoc-ref* argument "valueFrom")
+                                 (assoc-ref* argument "separate")))
 
   (define (collect-bindings ids+inputs+types+bindings)
-    (append-map id+input+type-tree+binding->command-line-binding
-                ids+inputs+types+bindings))
+    (map id+input+type-tree+binding->command-line-binding
+         ids+inputs+types+bindings))
 
   (define id+input+type-tree+binding->command-line-binding
     (match-lambda
@@ -399,31 +406,33 @@ be defined in the context in which the G-expressions are inserted."
                    (just (string->number position)))
                  ;; FIXME: Why a default value of 0?
                  0))
-               (prefix (maybe-assoc-ref binding "prefix")))
+               (prefix (maybe-assoc-ref binding "prefix"))
+               (separate? (maybe-bind binding (cut assoc-ref* <> "separate"))))
            (cond
             ;; Recurse over array types.
             ;; TODO: Implement record and enum types.
             ((cwl-array-type? matched-type)
-             (list (command-line-binding
-                    position
-                    prefix
-                    matched-type
-                    (append-map (lambda (i input)
-                                  (id+input+type-tree+binding->command-line-binding
-                                   (list (append id (list i))
-                                         input
-                                         (assoc-ref type-tree "items")
-                                         (maybe-assoc-ref (just type-tree)
-                                                          "inputBinding"))))
-                                (iota (vector-length input))
-                                (vector->list input))
-                    (maybe-assoc-ref binding "itemSeparator"))))
+             (command-line-binding
+              position
+              prefix
+              matched-type
+              (vector-map-indexed (lambda (i input)
+                                    (id+input+type-tree+binding->command-line-binding
+                                     (list (append id (list i))
+                                           input
+                                           (assoc-ref type-tree "items")
+                                           (maybe-assoc-ref (just type-tree)
+                                                            "inputBinding"))))
+                                  input)
+              separate?
+              (maybe-assoc-ref binding "itemSeparator")))
             (else
-             (list (command-line-binding position
-                                         prefix
-                                         matched-type
-                                         (apply json-ref inputs id)
-                                         %nothing)))))))))
+             (command-line-binding position
+                                   prefix
+                                   matched-type
+                                   (apply json-ref inputs id)
+                                   separate?
+                                   %nothing))))))))
 
   ;; For details of this algorithm, see ยง4.1 Input binding of the CWL
   ;; 1.2 CommandLineTool specification:
diff --git a/ravanan/workflow.scm b/ravanan/workflow.scm
index 2fd00d6..0451f68 100644
--- a/ravanan/workflow.scm
+++ b/ravanan/workflow.scm
@@ -279,11 +279,16 @@ command-line-tool)}."
                                             #())
                                         (or (assoc-ref cwl "hints")
                                             #()))
-                  (vector-map->list (lambda (input)
-                                      (let ((input-id (assoc-ref input "id")))
-                                        (cons input-id
-                                              (json-ref step "in" input-id))))
-                                    (assoc-ref run "inputs"))
+                  (vector-filter-map->list (lambda (input)
+                                             (let ((input-id (assoc-ref* input "id")))
+                                               (match (assoc input-id
+                                                             (assoc-ref* step "in"))
+                                                 ((_ . source)
+                                                  (cons input-id source))
+                                                 ;; Optional inputs may be
+                                                 ;; missing a source; drop them.
+                                                 (#f #f))))
+                                           (assoc-ref* run "inputs"))
                   ;; Inputs that either have a default or accept null values are
                   ;; optional.
                   (vector-filter-map->list (lambda (input)