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 /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/envs/latest/bin/cwltool 3.1.20240508115724
INFO Resolved 'expression.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/src/_includes/cwl/expressions/expression.cwl'
INFO [job expression.cwl] /tmp/2xgof1bk$ echo \
    -A \
    2 \
    -B \
    baz \
    -C \
    10 \
    9 \
    8 \
    7 \
    6 \
    5 \
    4 \
    3 \
    2 \
    1 > /tmp/2xgof1bk/output.txt
INFO [job expression.cwl] completed success
{
    "example_out": {
        "location": "file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/src/_includes/cwl/expressions/output.txt",
        "basename": "output.txt",
        "class": "File",
        "checksum": "sha1$a739a6ff72d660d32111265e508ed2fc91f01a7c",
        "size": 36,
        "path": "/home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/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 式は特定のフィールドにのみ使用できます。 以下はその例です:

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 /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/envs/latest/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-inline.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib-inline.cwl'
INFO [job hello-world-expressionlib-inline.cwl] /tmp/go95sd24$ 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 /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/envs/latest/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-external.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib-external.cwl'
INFO [job hello-world-expressionlib-external.cwl] /tmp/xkn0opaj$ 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 /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/envs/latest/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-language-user-guide-ja/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib.cwl'
INFO [job hello-world-expressionlib.cwl] /tmp/gm_5tg5i$ echo \
    'Hello World'
Hello World
INFO [job hello-world-expressionlib.cwl] completed success
{}INFO Final process status is success

注釈

$include ステートメントを使用すると、ローカルディスクまたはリモートロケーションからファイルをインクルードできます。相対パスと絶対パスの両方で動作します。CWL仕様の$includeについてを読むと、より詳しく知ることができます。