Skip to content

Commit ab59d30

Browse files
bentshermanchristopher-hakkaartpditommaso
authored
Fix bugs with workflow outputs (#5502)
Signed-off-by: Ben Sherman <[email protected]> Co-authored-by: Chris Hakkaart <[email protected]> Co-authored-by: Paolo Di Tommaso <[email protected]>
1 parent 4caf6ef commit ab59d30

File tree

15 files changed

+362
-153
lines changed

15 files changed

+362
-153
lines changed

docs/workflow.md

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ results/
465465
Target names cannot begin or end with a slash (`/`).
466466
:::
467467

468-
By default, all files emitted by a published channel will be published into the specified directory. If a channel emits list values, each file in the list (including nested lists) will be published. For example:
468+
By default, all files emitted by a published channel will be published into the specified directory. Lists and maps are recursively scanned for nested files. For example:
469469

470470
```nextflow
471471
workflow {
@@ -503,12 +503,12 @@ workflow {
503503
}
504504
505505
output {
506-
'foo' {
506+
foo {
507507
enabled params.save_foo
508508
path 'intermediates/foo'
509509
}
510510
511-
'bar' {
511+
bar {
512512
mode 'copy'
513513
}
514514
}
@@ -523,7 +523,7 @@ This output block has the following effect:
523523
See [Reference](#reference) for all available directives in the output block.
524524

525525
:::{tip}
526-
The output block is only needed if you want to customize the behavior of specific targets. If you are satisfied with the default behavior and don't need to customize anything, the output block can be omitted.
526+
The output block can be omitted if you are satisfied with the default behavior. However, as a best practice, you should declare each output in the output block, even if you don't need to customize the publish path or create any index files.
527527
:::
528528

529529
### Dynamic publish path
@@ -533,15 +533,17 @@ The `path` directive in a target block can also be a closure which defines a cus
533533
```nextflow
534534
workflow {
535535
main:
536-
ch_fastq = Channel.of( [ [id: 'SAMP1'], file('1.fastq'), file('2.fastq') ] )
536+
ch_samples = Channel.of(
537+
[id: 'SAMP1', fastq_1: file('1.fastq'), fastq_1: file('2.fastq')]
538+
)
537539
538540
publish:
539-
ch_fastq >> 'fastq'
541+
ch_samples >> 'samples'
540542
}
541543
542544
output {
543-
'fastq' {
544-
path { meta, fastq_1, fastq_2 -> "fastq/${meta.id}" }
545+
samples {
546+
path { sample -> "fastq/${sample.id}" }
545547
}
546548
}
547549
```
@@ -552,19 +554,15 @@ The closure can even define a different path for each individual file by returni
552554

553555
```nextflow
554556
output {
555-
'fastq' {
556-
path { meta, fastq_1, fastq_2 ->
557-
{ file -> "fastq/${meta.id}/${file.baseName}" }
557+
samples {
558+
path { sample ->
559+
{ filename -> "fastq/${sample.id}/${filename}" }
558560
}
559561
}
560562
}
561563
```
562564

563-
The inner closure will be applied to each file in the channel value, in this case `fastq_1` and `fastq_2`.
564-
565-
:::{tip}
566-
A mapping closure should usually have only one parameter. However, if the incoming values are tuples, the closure can specify a parameter for each tuple element for more convenient access, also known as "destructuring" or "unpacking".
567-
:::
565+
The inner closure will be applied to each file in the channel value, in this case `sample.fastq_1` and `sample.fastq_2`.
568566

569567
### Index files
570568

@@ -575,29 +573,29 @@ For example:
575573
```nextflow
576574
workflow {
577575
main:
578-
ch_fastq = Channel.of(
579-
[ [id: 1, name: 'sample 1'], '1a.fastq', '1b.fastq' ],
580-
[ [id: 2, name: 'sample 2'], '2a.fastq', '2b.fastq' ],
581-
[ [id: 3, name: 'sample 3'], '3a.fastq', '3b.fastq' ]
576+
ch_samples = Channel.of(
577+
[id: 1, name: 'sample 1', fastq_1: '1a.fastq', fastq_2: '1b.fastq'],
578+
[id: 2, name: 'sample 2', fastq_1: '2a.fastq', fastq_2: '2b.fastq'],
579+
[id: 3, name: 'sample 3', fastq_1: '3a.fastq', fastq_2: '3b.fastq']
582580
)
583581
584582
publish:
585-
ch_fastq >> 'fastq'
583+
ch_samples >> 'samples'
586584
}
587585
588586
output {
589-
'fastq' {
587+
samples {
588+
path 'fastq'
590589
index {
591-
path 'index.csv'
590+
path 'samples.csv'
592591
}
593592
}
594593
}
595594
```
596595

597-
The above example will write the following CSV file to `results/fastq/index.csv`:
596+
The above example will write the following CSV file to `results/samples.csv`:
598597

599-
```csv
600-
"id","name","fastq_1","fastq_2"
598+
```
601599
"1","sample 1","results/fastq/1a.fastq","results/fastq/1b.fastq"
602600
"2","sample 2","results/fastq/2a.fastq","results/fastq/2b.fastq"
603601
"3","sample 3","results/fastq/3a.fastq","results/fastq/3b.fastq"
@@ -607,14 +605,20 @@ You can customize the index file with additional directives, for example:
607605

608606
```nextflow
609607
index {
610-
path 'index.csv'
611-
header ['id', 'fastq_1', 'fastq_1']
612-
sep '\t'
613-
mapper { meta, fq_1, fq_2 -> meta + [fastq_1: fq_1, fastq_2: fq_2] }
608+
path 'samples.csv'
609+
header true
610+
sep '|'
614611
}
615612
```
616613

617-
This example will produce the same index file as above, but with the `name` column removed and with tabs instead of commas.
614+
This example will produce the following index file:
615+
616+
```
617+
"id"|"name"|"fastq_1"|"fastq_2"
618+
"1"|"sample 1"|"results/fastq/1a.fastq"|"results/fastq/1b.fastq"
619+
"2"|"sample 2"|"results/fastq/2a.fastq"|"results/fastq/2b.fastq"
620+
"3"|"sample 3"|"results/fastq/3a.fastq"|"results/fastq/3b.fastq"
621+
```
618622

619623
See [Reference](#reference) for the list of available index directives.
620624

modules/nextflow/src/main/groovy/nextflow/ast/NextflowDSLImpl.groovy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -549,13 +549,13 @@ class NextflowDSLImpl implements ASTTransformation {
549549
final block = (BlockStatement)closure.code
550550
for( Statement stmt : block.statements ) {
551551
if( stmt !instanceof ExpressionStatement ) {
552-
syntaxError(stmt, "Invalid publish target definition")
552+
syntaxError(stmt, "Invalid output target definition")
553553
return
554554
}
555555

556556
final stmtExpr = (ExpressionStatement)stmt
557557
if( stmtExpr.expression !instanceof MethodCallExpression ) {
558-
syntaxError(stmt, "Invalid publish target definition")
558+
syntaxError(stmt, "Invalid output target definition")
559559
return
560560
}
561561

@@ -564,7 +564,7 @@ class NextflowDSLImpl implements ASTTransformation {
564564

565565
final targetArgs = (ArgumentListExpression)call.arguments
566566
if( targetArgs.size() != 1 || targetArgs[0] !instanceof ClosureExpression ) {
567-
syntaxError(stmt, "Invalid publish target definition")
567+
syntaxError(stmt, "Invalid output target definition")
568568
return
569569
}
570570

modules/nextflow/src/main/groovy/nextflow/config/ConfigBuilder.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ class ConfigBuilder {
338338
binding.put('baseDir', base)
339339
binding.put('projectDir', base)
340340
binding.put('launchDir', Paths.get('.').toRealPath())
341+
binding.put('outputDir', Paths.get('results').complete())
341342
binding.put('secrets', SecretsLoader.secretContext())
342343
return binding
343344
}

0 commit comments

Comments
 (0)