问题
如何将多个工作流连接在一起?
目标
学习如何从多个CWL工作流描述构建嵌套工作流。
工作流是将多个工具组合起来执行更大的操作的方法。我们也可以把工作流看作是工具本身;一个CWL工作流可以作为另一个CWL工作流的一个步骤,如果工作流引擎支持子工作流特性需求:
requirements:
SubworkflowFeatureRequirement: {}
下面是一个使用1st-workflow.cwl作为一个嵌套的工作流:
nestedworkflows.cwl
#!/usr/bin/env cwl-runner
cwlVersion: v1.0
class: Workflow
inputs: []
outputs:
classout:
type: File
outputSource: compile/compiled_class
requirements:
SubworkflowFeatureRequirement: {}
steps:
compile:
run: 1st-workflow.cwl
in:
tarball: create-tar/tar_compressed_java_file
name_of_file_to_extract:
default: "Hello.java"
out: [compiled_class]
create-tar:
in: []
out: [tar_compressed_java_file]
run:
class: CommandLineTool
requirements:
InitialWorkDirRequirement:
listing:
- entryname: Hello.java
entry: |
public class Hello {
public static void main(String[] argv) {
System.out.println("Hello from Java");
}
}
inputs: []
baseCommand: [tar, --create, --file=hello.tar, Hello.java]
outputs:
tar_compressed_java_file:
type: File
streamable: true
outputBinding:
glob: "hello.tar"
CWL工作流可以像命令行工具一样作为一个步骤使用,它的CWL文件包含在run中。然后,工作流输入(inp和ex)和输出(classout)可以被映射为步骤的输入/输出。
compile:
run: 1st-workflow.cwl
in:
inp:
source: create-tar/tar
ex:
default: "Hello.java"
out: [classout]
我们1st-workflow.cwl是用工作流输入参数化的,所以在运行它时,我们必须提供一个作业文件来表示tar文件和*.java文件名。这通常是最佳实践,因为这意味着它可以在多个父工作流中重用,甚至在同一个工作流中的多个步骤中重用。
这里我们使用default:来硬编码“Hello”。作为ex输入,我们的工作流程也需要一个在inp的tar文件,我们将在create-tar步骤中准备它。在这一点上,重构第一个工作流可能是个好主意。使cwl具有更具体的输入/输出名称,因为这些名称也在其作为工具使用时出现。
还可以采用不那么通用的方法,避免作业文件中的外部依赖关系。因此,在这个工作流中,我们可以在将其添加到tar文件之前,使用前面提到的InitialWorkDirRequirement需求生成一个硬编码的Hello.java文件。
create-tar:
requirements:
InitialWorkDirRequirement:
listing:
- entryname: Hello.java
entry: |
public class Hello {
public static void main(String[] argv) {
System.out.println("Hello from Java");
}
}
在这种情况下,我们的步骤可以假设Hello.java,而不是参数化,所以只要CWL工作流引擎支持shellcommandrequire,我们就可以使用更简单的参数形式:
run:
class: CommandLineTool
requirements:
ShellCommandRequirement: {}
arguments:
- shellQuote: false
valueFrom: >
tar cf hello.tar Hello.java
注意这里使用了shellQuote: false,否则shell会尝试执行带引号的二进制文件”tar cf hello.tar Hello.java”。
这里的>块意味着换行符被剥离,因此可以在多行中编写单个命令。类似地,我们上面使用的|将保留换行符,并与shellcommandrerequirement相结合,这将允许嵌入shell脚本。不过,在CWL中应该少用Shell命令,因为这意味着你“跳出”了工作流,不再获得可重用的组件、来源或可伸缩性。为了再现性和可移植性,建议只使用带有DockerRequirement提示的shell命令,这样命令就可以在可预测的shell环境中执行。
你是否注意到我们没有将tar cf工具分割成一个单独的文件,而是将其嵌入到CWL工作流文件中?这通常不是最佳实践,因为工具无法重用。在本例中这样做的原因是,命令行是用文件名硬编码的,这些文件名只在此工作流中有意义。
在本例中,我们必须在外部准备一个tar文件,但这只是因为我们的内部工作流被设计为将其作为输入。对内部工作流更好的重构是采用一个要编译的Java文件列表,这将简化其在其他工作流中作为工具步骤的使用。
嵌套工作流是一个强大的特性,可以生成更高级的功能和可重用的工作流单元——但就像创建CWL工具描述一样,必须注意提高它在多个工作流中的可用性。