2.3. Expressions#
入力パラメータを操作する必要がある場合は、InlineJavascriptRequirement
を含めると、パラメータ参照が合法な場所であれば、CWL runnerによって評価されるJavascriptを使用できます。
重要
JavaScript の式は、絶対に必要な場合のみ使用するようにしてください。ファイル名、拡張子、パスなどを操作する場合は、basename
,nameroot
,nameext
, などの File
のビルトインプロパティ のいずれかを代わりに使用できるかどうかを検討してください。ベストプラクティスのリストを参照してください。
expression.cwl
##!/usr/bin/env cwl-runner
cwlVersion: v1.2
class: CommandLineTool
baseCommand: echo
requirements:
InlineJavascriptRequirement: {}
inputs: []
outputs:
example_out:
type: stdout
stdout: output.txt
arguments:
- prefix: -A
valueFrom: $(1+1)
- prefix: -B
valueFrom: $("/foo/bar/baz".split('/').slice(-1)[0])
- prefix: -C
valueFrom: |
${
var r = [];
for (var i = 10; i >= 1; i--) {
r.push(i);
}
return r;
}
このツールはinputs
を必要としないので、(ほとんど)空のジョブファイルで実行できます:
empty.yml
#{}
empty.yml
には、空のJSONオブジェクトの記述があります。JSONオブジェクトの記述は、中括弧{}
の中に含まれるので、空のオブジェクトは、単に空の括弧のセットで表されます。
次に、expression.cwl
を実行できます:
expression.cwl
を実行する#$ cwltool expression.cwl empty.yml
INFO /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'expression.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/expression.cwl'
INFO [job expression.cwl] /tmp/p2mcqit1$ echo \
-A \
2 \
-B \
baz \
-C \
10 \
9 \
8 \
7 \
6 \
5 \
4 \
3 \
2 \
1 > /tmp/p2mcqit1/output.txt
INFO [job expression.cwl] completed success
{
"example_out": {
"location": "file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/output.txt",
"basename": "output.txt",
"class": "File",
"checksum": "sha1$a739a6ff72d660d32111265e508ed2fc91f01a7c",
"size": 36,
"path": "/home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/output.txt"
}
}INFO Final process status is success
$ cat output.txt
-A 2 -B baz -C 10 9 8 7 6 5 4 3 2 1
なお、上の例のように、map構文で requirements を指定することも可能です:
requirements:
InlineJavascriptRequirement: {}
または配列として、各エントリ(この場合、class: InlineJavascriptRequirement
だけです)に-
を付けます。同じ構文で、追加のコマンドライン引数を定義します。
requirements:
- class: InlineJavascriptRequirement
JavaScriptはどこで使えますか?
パラメータ参照と同様に、JavaScript 式は特定のフィールドにのみ使用できます。 以下はその例です:
-
arguments
valueFrom
stdin
stdout
stderr
-
format
secondaryFiles
-
valueFrom
-
format
secondaryFiles
-
glob
outputEval
Workflow
からInputParameter と WorkflowOutputParameterから
format
secondaryFiles
steps
から-
valueFrom
-
-
expression
InputParameter と ExpressionToolOutputParameterから
format
secondaryFiles
-
coresMin
coresMax
ramMin
ramMax
tmpdirMin
tmpdirMax
outdirMin
outdirMax
-
listing
-
entry
entryname
EnvVarRequirement
から-
envValue
-
2.3.1. expressionLib
による外部ライブラリの利用とインラインJavaScriptコードの利用#
InlineJavascriptRequirement
は、expressionLib
属性をサポートし、ユーザーが外部 JavaScript ファイルを読み込んだり、インライン JavaScript コードを提供したりできるようにします。
expressionLib
属性に追加されたエントリーは、CWL runnerのJavaScriptエンジンで解析されます。これは、外部ファイルをインクルードしたり、CWL定義の他の部分で呼び出すことができるJavaScript関数を作成するために使用できます。
注釈
CWL標準(バージョン1.0から1.2)は、CWL定義で有効なJavaScriptのバージョンはECMAScript 5.1のみであると明記しています。これは、CWLドキュメントに含める、または書くコードは、ECMAScript 5.1に準拠していなければならないことを意味します。
例えば、InlineJavascriptRequirement
を使って、expressionLib
に JavaScript 関数をインラインで記述できます。その関数は、CWL定義の他の部分で使用できます:
hello-world-expressionlib-inline.cwl
#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- |
/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}
baseCommand: echo
inputs:
message:
type: string
arguments: [$( capitalizeWords(inputs.message) )]
outputs: []
このCWLワークフローを実行すると、JavaScript関数が起動し、echo
コマンドが入力メッセージの頭文字を大文字にして表示します:
hello-world-expressionlib-inline.cwl
実行しています。#$ cwltool hello-world-expressionlib-inline.cwl --message "hello world"
INFO /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-inline.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib-inline.cwl'
INFO [job hello-world-expressionlib-inline.cwl] /tmp/qmg1dvxq$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib-inline.cwl] completed success
{}INFO Final process status is success
capitalizeWords
関数を外部ファイルcustom-functions.js
に移動し、CWL 定義でインポートしてみましょう:
custom-functions.js
#/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}
hello-world-expressionlib-external.cwl
#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }
baseCommand: echo
inputs:
message:
type: string
arguments: [$( capitalizeWords(inputs.message) )]
outputs: []
custom-functions.js
ファイルは、$include: custom-functions.js
という記述でCWL定義に含まれます。これにより、関数と変数がCWL定義の他の部分で使用できるようになります。
hello-world-expressionlib-external.cwl
実行しています。#$ cwltool hello-world-expressionlib-external.cwl --message "hello world"
INFO /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-external.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib-external.cwl'
INFO [job hello-world-expressionlib-external.cwl] /tmp/1wvecunq$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib-external.cwl] completed success
{}INFO Final process status is success
最後に、CWL定義にはインラインと外部JavaScriptコードの両方が存在できることに注意してください。この最後の例では、expressionLib
属性に、新しい関数createHelloWorldMessage
を追加しました。この関数は、外部ファイルcustom-functions.js
からcapitalizeWords
関数を呼び出しています。
hello-world-expressionlib.cwl
#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }
- |
/**
* A merely illustrative example function that uses a function
* from the included custom-functions.js file to create a
* Hello World message.
*
* @param {Object} message - CWL document input message
*/
var createHelloWorldMessage = function (message) {
return capitalizeWords(message);
};
baseCommand: echo
inputs:
message:
type: string
arguments: [$( createHelloWorldMessage(inputs.message) )]
outputs: []
hello-world-expressionlib.cwl
を実行する。#$ cwltool hello-world-expressionlib.cwl --message "hello world"
INFO /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib.cwl'
INFO [job hello-world-expressionlib.cwl] /tmp/ecdux005$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib.cwl] completed success
{}INFO Final process status is success
注釈
$include
ステートメントを使用すると、ローカルディスクまたはリモートロケーションからファイルをインクルードできます。相対パスと絶対パスの両方で動作します。CWL仕様の$include
についてを読むと、より詳しく知ることができます。