mainvisual

みなさん、gulp使ってますか? 自分は、ひどいgulpユーザーです。 このサイトも一部でgulpを使っていて、非常に重宝しています。

それで、時々gulpからgulpを呼びたいなーって思う時があったわけです。 そういう時に役立つお話です。

gulpからgulpを呼び出すには、nodejsに実装されているchild_processを使います。 child_processは、メインプロセスから、フォークして子プロセスを作るライブラリがまとまっています。

今回はこれを使って、gulpからgulpを呼び出します。

(child_processから返却されるオブジェクトを使うと、リアルタイムで子プロセスの標準出力を取得できるんですが、これについては別の記事で紹介します。)

child_processを使って、子プロセスを生成する関数の例

使うスクリプトはこんな感じです。

function _run_cmd(cmd, args, option) {
  option = option || {};
  var spawn = require('child_process').spawn,
  child = spawn(cmd, args, option);
  return child;
}

function cmd_exec(cmd, args, option, callback){
  option = option || {}
  var child = _run_cmd(cmd, args, option);
  var tag = '\u001b[36m'+'['+cmd+'] '+'\u001b[0m'
  child.stdout.setEncoding('utf-8');
  child.stdout.on('data', (data) => {
    process.stdout.write(tag + data);
  });
  child.stderr.setEncoding('utf-8');
  child.stderr.on('data', (data) => {
    process.stdout.write(tag + '\u001b[33m' + data + '\u001b[0m');
  });
  child.on('exit', (code) => {
    if(code == 0)
      console.log(tag + '\u001b[32m'+'child process exited with code ' + code + '\u001b[0m');
    else
      console.log(tag + '\u001b[31m'+'child process exited with code ' + code + '\u001b[0m');
    if (typeof callback !== "undefined" && callback !== null) callback();
  });
  return child;
}

cmd_exec関数の説明

親プロセスとなるgulpファイルに上のソースコードをコピーします。 子プロセスを呼び出すには、cmd_exec関数を使います。

以下は、関数の説明です。

cmd_exec(cmd, args, option, callback)
  • cmd {String} 実行するコマンド(例: gulp)
  • args {Array} 文字列による引数の配列(例: build)
  • options {Object}
    • cwd {String} 子プロセスのカレントワーキングディレクトリ
    • stdio {Array|String} 子プロセスの標準入出力の設定 (後述)。
    • customFds {Array} Deprecated 子プロセスが標準入出力として使用する ファイル記述子の配列 (後述)
    • env {Object} 環境変数として与えるキー・値のペア
    • detached {Boolean} 子プロセスがプロセスグループのリーダになるかどうか (後述)。
    • uid {Number} プロセスのユーザ ID を設定します (setuid(2) を参照してください)。
    • gid {Number} プロセスのグループ ID を設定します (setgid(2) を参照してください)。
  • callback {Function} コマンドの終了後に呼ばれる関数

cmd_exec関数の使い方

cmdにコマンド名、argsに配列にオプションを入れます。

lsコマンドを例にするとこんな感じですね。

コマンドの例

gulpからgulpを呼ぶ方法

例えば、このようなフォルダ階層になっていたとします。

.
├── gulpfile.js
└── submodule
    └── gulpfile.js

その場合、gulpからgulpを呼び出すには、このようにします。

var gulp_child = cmd_exec('gulp', ['build'], {
  cwd: process.cwd()+"/submodule/"
});

cwdオプションを使って、カレントディレクトリを移動させます。 この時、絶対パスで指定した方がトラブルにならないので、process.cwd()を使ってやります。

gulpfile.jsの例

総合すると、こんな感じになります。 フォルダ構造はさっきと同じです。

(もう一度掲載)

.
├── gulpfile.js
└── submodule
    └── gulpfile.js
var gulp = require('gulp');

function run_cmd(cmd, args, option) {
  option = option || {};
  var spawn = require('child_process').spawn,
  child = spawn(cmd, args, option);
  return child;
}

function cmd_exec(cmd, args, option, callback){
  option = option || {}
  var child = run_cmd(cmd, args, option);
  var tag = '\u001b[36m'+'['+cmd+'] '+'\u001b[0m'
  child.stdout.setEncoding('utf-8');
  child.stdout.on('data', (data) => {
    process.stdout.write(tag + data);
  });
  child.stderr.setEncoding('utf-8');
  child.stderr.on('data', (data) => {
    process.stdout.write(tag + '\u001b[33m' + data + '\u001b[0m');
  });
  child.on('exit', (code) => {
    if(code == 0)
      console.log(tag + '\u001b[32m'+'child process exited with code ' + code + '\u001b[0m');
    else
      console.log(tag + '\u001b[31m'+'child process exited with code ' + code + '\u001b[0m');
    if (typeof callback !== "undefined" && callback !== null) callback();
  });
  return child;
}

gulp.task('default', () => {
  var gulp_child = cmd_exec('gulp', ['build'], {
    cwd: process.cwd()+"/submodule/"
  });
});

まとめ

gulpからgulpを呼び出す方法はググってもあまりなかったので、 自分で作るのに苦労しました。

特に、require('child_process').spawnについてのドキュメントがなかったので、 思った以上に使い方を探すのに苦労しました。

cmd_exec関数を作ったので、幾分か扱いが簡単になりましたが、 まだ改善の余地ありそうです。