npm 2.15ではdevtoolsetのgccでビルドできません

Node.js v4系のネイティブモジュールをビルドするにはC++11対応のコンパイラが必要です。

CentOS 6のgccは4.4と古くてビルドすることができないのですが、devtoolsetでgcc 4.8を入れることでネイティブモジュールを npm install できるようになります。

sudo wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo
sudo yum install scl-utils
sudo yum install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils
scl enable devtoolset-2 -- npm i

しかし、Node.js v4.6に付属しているnpm 2.15ではdevtoolsetを設定しても、gcc 4.4でビルドされてエラーになってしまうのでした。

$ npm -v
2.14.12
$ npm run nyan

> test@1.0.0 nyan /home/nullpon/test
> echo $PATH

/usr/lib/node_modules/npm/bin/node-gyp-bin:/home/nullpon/test/node_modules/.bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/nullpon/bin
$ npm -v
2.15.9
$ npm run nyan

> test@1.0.0 nyan /home/nullpon/test
> echo $PATH

/usr/lib/node_modules/npm/bin/node-gyp-bin:/home/nullpon/test/node_modules/.bin:/usr/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/nullpon/bin

んー、これはひどい。2.15.9では元々のPATHの前に /usr/bin が付加されてしまっています。どうりで scl enable devtoolset-2gcc 4.8にパスを通しても /usr/bin/gcc が使われてしまうわけだクソッタレ!

なお、この糞挙動はnpmのmasterでは既に修正されていますが、Node.js v4付属のnpm 2にはバックポートされておりません。LTSとは一体何なのか…

応急処置としてnpm 2.14をインストールすればビルドできるようになります。

sudo npm install -g npm@2.14.12

npmをダウングレードしたくない場合は、環境変数CC、CXXを設定すれば良さそうです。

export CC=/opt/rh/devtoolset-2/root/usr/bin/gcc
export CXX=/opt/rh/devtoolset-2/root/usr/bin/g++
npm i 

もしくはCentOS 6のサーバを窓から投げ捨てて、CentOS 7にするのも良いでしょう。あとはとっととNode v6に上げるとか。v6付属のnpm 3なら修正済みかもしれません。知らんけど。

node.jsでモジュールを再ロードする

requireされたモジュールは、モジュールのフルパスをキーとしてrequire.cacheというオブジェクトに格納されており、これを削除すればモジュールを再びロードできるらしい。というわけでテスト。

# main.js
console.log(process.pid);

setInterval(function() {
    var a = require('./a');
    console.log(a.value);
}, 3000);

process.on('SIGINT', function() {
    console.log('clear module a');
    var key = require.resolve('./a');
    delete require.cache[key];
});
# a.js
console.log('load module a');

exports.value = 'wang';

実行すると、3秒毎にwangと書かれる。キャッシュされるので load module a というログは1回しかでない。

$ node hoge.js
13330
load module a
wang
wang
wang
wang
wang

別のターミナルでa.jsを編集する

# a.js
console.log('load module a');

exports.value = 'nyan';

a.jsを書き換えて保存し、

$ kill -SIGINT 13330

nodeプロセスにSIGINTを投げると…

wang
clear module a
load module a
nyan
nyan
nyan

再度 load module a が出力され、書き換えた内容が反映されている。モジュールがロードしなおされたようだ。

開発用のhot reloadingに使えると思うが、既存のアプリに適用するには設計レベルで見直しが必要なので難しそう。hot reloading機能付きのフレームワークを設計するならアリでしょう。