Gulp でビルドスクリプトを書く(PureScript で Hello, World!)
- 第1話 PureScript ver. 0.7.0 以上での Hello, World!
- 第2話 PureScript ver. 0.7.0 以上で “ブラウザーで” Hello, World!
- 第3話 NPM と Bower に依存を管理させる(PureScript で Hello, World!)
これらに続く第4話。前回では NPM の scripts
を使ってコマンドを書いてビルドをしていたんだが、sh と cmd と2つ管理しないといけなかったり、ワンライナーなんで保守がしづらかったりと問題があるので、今回はビルドツールである Gulp を使って PureScript のコードをビルドするようにする。
おさらい
前回使ったワンライナーはこんなんだった。
bower install && psc --ffi bower_components\purescript-*\src\**\*.js bower_components\purescript-*\src\**\*.purs src\Main.purs && psc-bundle output\*\*.js --module Main --main Main -o output\main.js && copy src\index.html output\
4つのステップがあるのでそれぞれ見ていく。
bower install
bower.json に記載した依存をインストールする。
psc --ffi bower_components\purescript-*\src\**\*.js bower_components\purescript-*\src\**\*.purs src\Main.purs
PureScript ソースコードをコンパイルして JavaScript に。
psc-bundle output\*\*.js --module Main --main Main -o output\main.js
CommonJS 仕様の JS コードをブラウザーで実行できるように変換する。
copy src\index.html output\
JS を読み込む HTML を成果物フォルダーにコピーする。
bower install
は初回にこれをそのまま実行してもらうとして、それ以外を Gulp スクリプトに変換していく。
gulpfile.js
Gulp スクリプトは単なる JS のコードで require('gulp')
すると使える内部 DSL である。今のワンライナーでは psc
コマンドで生成される途中成果物と psc-bundle
した後の最終成果物が同じフォルダー下にあって管理の問題上分けたいので、最終成果物は dest フォルダー下に置かれるようにする。そうすると、所望の Gulp スクリプトは下記のようになる。これを gulpfile.js として保存する。
'use strict'; var gulp = require('gulp'); var purescript = require('gulp-purescript'); var sources = [ 'src/**/*.purs', 'bower_components/purescript-*/src/**/*.purs', ]; var foreigns = [ 'src/**/*.js', 'bower_components/purescript-*/src/**/*.js' ]; var destination = 'dest'; gulp.task('make', function () { return purescript.psc({ src: sources, ffi: foreigns }); }); gulp.task('bundle', ['make'], function () { return purescript.pscBundle({ src: 'output/**/*.js', output: destination + '/main.js', module: 'Main', main: 'Main' }); }); gulp.task('copy', function () { return gulp.src(['src/**/*.html']) .pipe(gulp.dest(destination)); }); gulp.task('default', ['bundle', 'copy']);
雰囲気でだいたい分かると思うが簡単に説明すると、require('gulp-purescript')
で Gupl スクリプト内で PureScript 固有の記述ができるようになる。gulp.task
関数は第1引数にタスク名、第2引数に依存するタスクの配列、第3引数にタスクの内容のクロージャーを渡すようになっている。第2・第3引数は省略することができる。
このようにタスクを記述すると下記のようにタスクを実行することができるようになる。
> gulp make
引数なしで gulp
とだけ実行すると default
タスクが実行される。
依存の追加
新たに、Gulp を使用するようになったので package.json の devDependencies
に gulp
と gulp-purescript
を追加する。
"devDependencies": { "purescript": "0.7.2", "bower": "^1.4.1", "gulp": "^3.9.0", "gulp-purescript": "^0.7.0" }
NPM でインストールした Bower と Gulp を呼べるように package.json の scripts
を下記のようにする。当然既存のワンライナーのビルドコマンドはもう要らない。
"scripts": { "bower": "bower", "gulp": "gulp" }
ビルド手順
以上をまとめるとビルド手順は下記となる。
> npm install > npm bower install > npm gulp
ビルドを少し速くする
今の copy タスクでは前回ビルドから変更点がなくてもコピーしてしまっているので、最終変更日時を確認して必要のあるときだけコピーするようにする。変更点は下記である。
package.json
"devDependencies": { … "gulp-newer": "^0.5.1" }
gulpfile.js
var newer = require('gulp-newer') … gulp.task('copy', function () { return gulp.src(['src/**/*.html']) .pipe(newer(destination)) .pipe(gulp.dest(destination)); });
実行するコマンドに変更はない。
NPM と Bower に依存を管理させる(PureScript で Hello, World!)
前々回の記事「PureScript ver. 0.7.0 以上での Hello, World!」、前回の記事「PureScript ver. 0.7.0 以上で “ブラウザーで” Hello, World!」に次ぐ記事。
NPM の package.json とか Bower の bower.json とかに依存を書いてビルドを簡単にするのが今回の目的。
おさらいと前提
ファイル一覧。
> dir /b src > dir /b src index.html Main.purs
ファイルの中身。src\index.html は下記。
<html><head><script type="text/javascript" src="main.js" ></script>
src\Main.purs は下記。
module Main where import Control.Monad.Eff.Console (log) main = log "Hello, World!"
NPM と Bower と依存するパッケージはすでにインストールしてある前提。
package.json
NPM の設定ファイルは package.json。npm init
コマンドでいくつか質問に答えれば下記のような package.json ができるはず。
{ "name": "purescript-hello-console", "version": "1.0.0", "main": "index.html", "dependencies": {}, "devDependencies": { "bower": "^1.4.1", "purescript": "^0.7.0" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+ssh://git@github.com/kakkun61/purescript-playground.git" }, "author": "Kazuki Okamoto", "license": "MIT", "bugs": { "url": "https://github.com/kakkun61/purescript-playground/issues" }, "homepage": "https://github.com/kakkun61/purescript-playground", "description": "" }
dependencies
のバージョン番号のところの ^
はセマンティックバージョンを考慮して互換性のあるバージョンを表す NPM の文法らしい。
bower.json
Bower の設定ファイルは bower.json。NPM と同じように bower init
コマンドで質問に答えていけば下記のような bower.json ができるはず。
{ "name": "purescript-hello-console", "version": "1.0.0", "main": "index.html", "authors": [ "Kazuki Okamoto" ], "license": "MIT", "homepage": "https://github.com/kakkun61/purescript-playground", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "purescript-console": "~0.1.0" } }
~
も ^
みたいなものっぽいけどいまいち分かってないので「node.js - What is the bower version syntax? - Stack Overflow」読んどいて。(丸投げ
ここまでで npm install
、bower install
で依存をインストールすることはできるようにはなるけどビルドは手打ちなんでスクリプトにしておく。
NPM のスクリプト機能
package.json の scripts
の項目に下記を追記する。
"build": "bower install && psc --ffi 'bower_components/purescript-*/src/**/*.js' 'bower_components/purescript-*/src/**/*.purs' src/Main.purs && psc-bundle output/*/*.js --module Main --main Main -o output/main.js && cp src/index.html output/", "build-cmd": "bower install && psc --ffi bower_components\\purescript-*\\src\\**\\*.js bower_components\\purescript-*\\src\\**\\*.purs src\\Main.purs && psc-bundle output\\*\\*.js --module Main --main Main -o output\\main.js && copy src\\index.html output\\",
ユーザーが NPM で Bower をインストールしたことを考えると bower
コマンドにパスが通ってない可能性があるので $(npm bin)/bower
ってしておくべきなんだけど、package.json の中に書く場合は npm bin
がパスに追加されている。($(npm bin)/bower
だけ cmd じゃない…… cmd で同じことしようとすると for /f "usebackq" %p in (`npm bin`) do %p\bower
とかになるはず。)
ここまで準備するとユーザーは NPM をインストールした後、下記コマンドでビルドしできた output\index.html をブラウザーで開けばよい。
> npm install > npm run build-cmd
編集
2015.10.18
package.json で dependencies
に記述していた bower
と purescript
を devDependencies
に移した。これらは成果物が依存しているわけではなく開発時にのみ依存するため。
PureScript ver. 0.7.0 以上で “ブラウザーで” Hello, World!
先日の記事では Node で Hello, World! するところまでできた。では、それをブラウザーで動かすにはどうするんだって思って調べてうまくいったのでまとめる。
CommonJS
psc コマンドで吐いた Javascript には require
関数が使われてて複数のファイルに分かれていてこいつどうするんだって思ってたんだけど、これは CommonJS っていう仕様なのね。ブラウザーは CommonJS に対応してないので(全部がそうか調べたわけじゃないけど)こいつをなくす変換をする必要がある。
Browserify
「require
ブラウザー」とかで検索すると Browserify (とか RequireJS)が引かっかるのでそれを使うのかと思ったんだけど、これを使わなくていいみたい。
psc-bundle
この Wiki「PureScript Without Node」に書いてあって psc-bundle コマンドを使えばいい。psc をインストールしたときについてきているはず。このコマンドを使うとモジュールに分かれた JS を1つにまとめた JS にしてくれる。
> psc-bundle output\*\*.js --module Main --main Main -o main.js
--module
オプションと引数で、どのモジュールがエントリーポイントなのかを指定する。そのモジュールから辿れない関数は成果物から除去される。このオプションがなければ依存モジュールの全部の関数が成果物に含まれる。
--main
オプションと引数で、実行される main
関数のあるモジュールを指定する。JS 読み込み時に実行したいわけではなかったらこのオプションは要らない。
-o
オプションと引数でどのファイルに出力するかを指定する。なければ標準出力に出力される。
残りの引数には psc コマンドで生成された JS を指定する。
これで先日の Main.purs をコンパイルした JS をバンドルすると下記 JS が生成される。
// Generated by psc-bundle 0.7.1.0 var PS = { }; (function(exports) { /* global exports, console */ "use strict"; // module Control.Monad.Eff.Console exports.log = function (s) { return function () { console.log(s); return {}; }; }; })(PS["Control.Monad.Eff.Console"] = PS["Control.Monad.Eff.Console"] || {}); (function(exports) { // Generated by psc version 0.7.1.0 "use strict"; var $foreign = PS["Control.Monad.Eff.Console"]; var Prelude = PS["Prelude"]; var Control_Monad_Eff = PS["Control.Monad.Eff"]; exports["log"] = $foreign.log;; })(PS["Control.Monad.Eff.Console"] = PS["Control.Monad.Eff.Console"] || {}); (function(exports) { // Generated by psc version 0.7.1.0 "use strict"; var Control_Monad_Eff_Console = PS["Control.Monad.Eff.Console"]; var main = Control_Monad_Eff_Console.log("Hello, World!"); exports["main"] = main;; })(PS["Main"] = PS["Main"] || {}); PS["Main"].main();
そして下記の HTML ファイルを生成してブラウザーに読ませると無事ブラウザーのコンソールに Hello, World! が表示される。
<html><head><script type="text/javascript" src="main.js" ></script>
参考
- PureScript Without Node · purescript/purescript Wiki
- ちょうど昨日か一昨日あたりに公開されたみたい
PureScript ver. 0.7.0 以上での Hello, World!
PureScript 0.7.1.0 で Node でコンソールに Hello, World! するところまでできたのでそのまとめ。
実例による PureScript を読んで勉強していたんだけど、先月終わりのバージョンアップの関係でそのままでは動かなくって調べたんでまとめる。英語の本家の方のドキュメントもこれから対応させていくらしい。
コマンドは Windows コマンドプロンプトのを使っているので UNIX 系の人は適宜読み替えること。
環境構築
用意するものは、PureScript コンパイラー(psc)と Node(NPM も使う)と Bower。(好みによって Cabal も。)NPM と Bower (と Cabal)がどういう使い分けをされてるかは以下な感じ。
- NPM
- Bower
- purescript-prelude のインストール
- ライブラリーの管理はこっちでするっぽい
- Cabal
psc のインストール
Homebrew なり Chocolatey なり NPM なり Cabal なり、直接バイナリーをインストールするなりする。詳しくはこちら。
Cabal というか Stack が楽なので Stack でインストールする方法は次の通り。
Stack はここからバイナリーをダウンロード。stack setup
して、stack install purescript
すると Stackage にないファイルが必要だといわれるのでそれを設定ファイルに記述する。設定ファイルの場所は Windows なら %USERPROFILE%\AppData\Roaming\stack\global\stack.yaml。さっきのエラーにファイルの場所が書かれている。設定ファイルに追記するものもエラーに書かれているのでそれを下記のように追記する。
extra-deps: - aeson-better-errors-0.9.0 - bower-json-0.7.0.0 - boxes-0.1.4 - pattern-arrows-0.0.2
で、もう1度 stack install purescript
するとインストールされる。どこに実行ファイルが保存されたかが表示されるのでパスを通す。
Node と NPM のインストール
apt-get なり Homebrew なり Chocolatey なり直接インストーラーでインストールするなりする。
Bower のインストール
> npm install -g bower
Hello World プログラム
下記が 0.7.0 以降での Hello World プログラム。Main.purs で保存する。
module Main where import Control.Monad.Eff.Console (log) main = log "Hello, World!"
0.7.0 より前のは下記。
module Main where import Debug.Trace main = trace "Hello, World!"
0.7.0 での変更点から今回のプログラムに関係するものを取り出すと下記になる。移行ガイドもある。
コンパイル
先のプログラムが依存するモジュールをインストールする。
> dir Main.purs > bower install purescript-console > dir bower_components purescript-console purescript-eff purescript-prelude
コンパイルする。
> psc --ffi 'bower_components\purescript-*\src\**\*.js' 'bower_components\purescript-*\src\**\*.purs' Main.purs > dir output Control.Monad.Eff Control.Monad.Eff.Class Control.Monad.Eff.Console Control.Monad.Eff.Unsafe Main Prelude
実行
生成された output\Main\index.js ファイルを見ると main
関数をエクスポートするだけのプログラムになっているので、実行する Javascript を書く。ここでは main.js としておく。
require("Main").main();
Node で require するためのパスを指定して実行する。
> set NODE_PATH=%cd%\output > node main.js Hello, World!
参考
はてなブログへ移行
1回移って戻ってきたけどやっと移った。
はてなブログに移ってみます - 趣味はデバッグ……
はてなブログから戻った。 - 趣味はデバッグ……
stack コマンドを使ってみた
6月9日に FP Complete から stack コマンドがリリースされました(公式ブログ)。とりあえずブログ1文目を訳すとこんな感じです。
stack は新しく、完璧で、クロスプラットフォームで動作する開発ツールで、新参も古参も対象にしていて、コンパイラーをインストールしてセットアップして、必要なパッケージをインストールして、ビルドして、プロジェクト内の1つ以上のパッケージのテストをするもしくはベンチマークを取るためのものです。
stack is a new, complete, cross-platform development tool aimed at both new and experienced Haskell developers alike, for installing and setting up the compiler, installing packages needed, and building, testing or benchmarking one or more packages in a project at a time.
とりあえずこれさえ入れておけば Haskell に必要なコマンドはこれで完結するというような謳い文句です。1つのプロジェクトをビルドしたら別のプロジェクトがビルドできなくなったというようないわゆる deps hell も解決すると書かれています。
stack コマンド
stack ghc
で GHC が起動します。インストールされていなければダウンロードしてインストールからしてくれます。同じように stack ghci
、stack runghc
が使えます。
stack build
でプロジェクトのビルドができます。設定は stack.yaml
と project-name.cabal
ファイルに書きます。stack は cabal を置き換えるものらしいですが、.cabal ファイルは使うようです。
cabal install
に当たるようなコマンドはなさそうです。
設定ファイル
.cabal ファイルは cabal が使っていたものと同じです。stack.yaml
は Stackage のどのバージョンを使用するかを表す resolver
が必須になります。なので、最低限下記な感じ。
resolver: lts-2.13
Stackage に存在しないパッケージを利用する場合は extra-deps
を追記します。
extra-deps: - foo-1.2.3.4 - bar-9.8.7.6
パッケージ
インストールされるパッケージは、GHC と一緒についてくるものについては global に、Stackage にあるものについては snapshot に、extra-deps で指定したものについては local に保存されます。global には追加では一切パッケージは保存されなくて、snapshot はプロジェクト間で共有、local はプロジェクトごとに固有になっていて、deps hell を避けるようになっています。
感想
とりあえず Windows での Haskell 環境がめっちゃ楽になったぞい、です。
ちなみにまだバージョン 0.0.0 ですが、0.1 が出ると tanakh さんがもっと詳しい記事を書いてくれると思います。
stack-0.1が出たら解説記事書く
— Hideyuki Tanaka (@tanakh) June 11, 2015
今見たら 0.0.1 は欠番?で 0.0.2 が出てた。
(2015.06.25 追記) tanakh さんがもう少し詳しくて新しい情報をまとめてくれました。
TEX Yoda TrackPoint Keyboard が来た&組み立てた
トラックポイントが付いてて Cherry MX スイッチなキーボードがクラウドファンディングで買えたのでポチった。
TEX Yoda TrackPoint Keyboard ってやつです。 特徴としては下記な感じ。
- Cherry MX スイッチ(注文時に何色か選べる)
- トラックポイント
- 自分で組み立て(追加料金で組み立て済みもあった)
1月30日に購入して4月3日にアメリカ合衆国から発送されてドイツ経由で到着が4月15日。事業を始めたいっていうクラウドファンディングじゃなくて共同購入みたいな感じで購入者が多くなるほど廉くなるっていうしくみで、お値段 $199.99、送料込みで $261.47 でした。あと、日本の消費税が1000円と税関手数料が200円でそれは配達に来た郵便局員に手渡しました。消費税はどういう計算なんだろう…… そもそも税関で値段分からないような?
クラウドファンディングのサービスのロゴの入った箱とシールで来ました。
税関で箱開けられたみたいで郵便局のシールがその上に貼られてました。
量産品じゃないので TEX Beetle ってやつの箱に入ってました。
中身は筐体と回路基板とスイッチとキートップで、スイッチを1つずつ基板に半田付けしていきます。
スイッチをアルミ板にはめていって基板の穴にスイッチの端子を通したところ。キーの2倍の数の端子を穴に通さなくっちゃいけなくて結構手間取りました。
せっかく自分で半田付けするんで、秋葉原の千石電商で追加のスイッチを買って場所によってスイッチを変えてみました。基本はキーボードと一緒に届いた黒軸で、修飾キーには黒より軽い赤軸を、マウスボタンにはクリック感のある茶軸を使ってみました。修飾キーを軽くしたのは小指で使うからで、マウスボタンはクリック感のあった方がいいかなとなんとなくです。追加のスイッチだけで2500円。
ここに組み立て手順書があるんですけど、単に「LED を付ける」って書いてあるところで実はスペーサーをかますべきだったみたいでした。後からパーツが余ったので気付きました。ただ、キートップが黒だとキーに埋もれるように LED が来てしまって分かりにくいのでスペーサーをかまさなかったのはわりとよかったです。
後はキートップを被せて完成。元々使ってた Filco Majestouch と。
裏に DIP スイッチがあって修飾キーの配置を変えられるので、OS でのリマップではどうにもならない Fn キーの位置だけを好みの場所に合わせました。
ちょっと値段が高かったけどトラックポイントが結構快適で満足な買い物でした。
一つ、箱の中の仕切りの紙がなぜ鏡文字だったかだけが謎。