All Articles

MacでHaxeの開発環境→HelloWorldまでやってみた

先日の大なごやJSに参加して、最近流行りのJavaScriptプリプロセッサの中でも一番おもしろそうだった && 実際に案件に投入(しかも大規模)されているということで Haxeに興味を持ったので、さっそくどんなもんかと使い始めてみました。まずは環境構築からHello Worldまでということで。

なお、発表してくださった@terurouさんは日本語でチュートリアルを書いてくださっています。これはありがたい!

Haxe/JavaScriptチュートリアル

上記を参考に進めます。環境はMac OSX 10.7.4です。

Haxeのインストール

公式サイトからMac用のユニバーサルインストーラをダウンロードしてdmgファイルからインストール。簡単に入りますね。以下のようにターミナルでコマンドが発行できれば成功みたいです。

オプションなどはおいおい調べていきますが、まずはやってみます。

ターミナルからコンパイル

多分ちゃんとした開発はIDEにしますが、せっかくコマンドがあるのでまずは試しにコンパイルしてみます。書いてみたコードはこんな感じで、helloworld.hxとします。

package;

import js.Lib;

class HelloWorld {

        static function main() {
                js.Lib.alert('Hello World');
        }
}

このhxファイルをターミナルからコンパイルしてみます。

$ haxe -main HelloWorld -js helloworld.js -cp $HOME --js-modern

すると、以下のようなJSファイルができました!(長いです)

(function () { "use strict";
var HelloWorld = function() { }
HelloWorld.__name__ = true;
HelloWorld.main = function() {
	js.Lib.alert("Hello World");
}
var HxOverrides = function() { }
HxOverrides.__name__ = true;
HxOverrides.dateStr = function(date) {
	var m = date.getMonth() + 1;
	var d = date.getDate();
	var h = date.getHours();
	var mi = date.getMinutes();
	var s = date.getSeconds();
	return date.getFullYear() + "-" + (m < 10?"0" + m:"" + m) + "-" + (d < 10?"0" + d:"" + d) + " " + (h < 10?"0" + h:"" + h) + ":" + (mi < 10?"0" + mi:"" + mi) + ":" + (s < 10?"0" + s:"" + s);
}
HxOverrides.strDate = function(s) {
	switch(s.length) {
	case 8:
		var k = s.split(":");
		var d = new Date();
		d.setTime(0);
		d.setUTCHours(k[0]);
		d.setUTCMinutes(k[1]);
		d.setUTCSeconds(k[2]);
		return d;
	case 10:
		var k = s.split("-");
		return new Date(k[0],k[1] - 1,k[2],0,0,0);
	case 19:
		var k = s.split(" ");
		var y = k[0].split("-");
		var t = k[1].split(":");
		return new Date(y[0],y[1] - 1,y[2],t[0],t[1],t[2]);
	default:
		throw "Invalid date format : " + s;
	}
}
HxOverrides.cca = function(s,index) {
	var x = s.charCodeAt(index);
	if(x != x) return undefined;
	return x;
}
HxOverrides.substr = function(s,pos,len) {
	if(pos != null && pos != 0 && len != null && len < 0) return "";
	if(len == null) len = s.length;
	if(pos < 0) {
		pos = s.length + pos;
		if(pos < 0) pos = 0;
	} else if(len < 0) len = s.length + len - pos;
	return s.substr(pos,len);
}
HxOverrides.remove = function(a,obj) {
	var i = 0;
	var l = a.length;
	while(i < l) {
		if(a[i] == obj) {
			a.splice(i,1);
			return true;
		}
		i++;
	}
	return false;
}
HxOverrides.iter = function(a) {
	return { cur : 0, arr : a, hasNext : function() {
		return this.cur < this.arr.length;
	}, next : function() {
		return this.arr[this.cur++];
	}};
}
var IntIter = function(min,max) {
	this.min = min;
	this.max = max;
};
IntIter.__name__ = true;
IntIter.prototype = {
	next: function() {
		return this.min++;
	}
	,hasNext: function() {
		return this.min < this.max;
	}
	,__class__: IntIter
}
var Std = function() { }
Std.__name__ = true;
Std["is"] = function(v,t) {
	return js.Boot.__instanceof(v,t);
}
Std.string = function(s) {
	return js.Boot.__string_rec(s,"");
}
Std["int"] = function(x) {
	return x | 0;
}
Std.parseInt = function(x) {
	var v = parseInt(x,10);
	if(v == 0 && (HxOverrides.cca(x,1) == 120 || HxOverrides.cca(x,1) == 88)) v = parseInt(x);
	if(isNaN(v)) return null;
	return v;
}
Std.parseFloat = function(x) {
	return parseFloat(x);
}
Std.random = function(x) {
	return Math.floor(Math.random() * x);
}
var js = {}
js.Boot = function() { }
js.Boot.__name__ = true;
js.Boot.__unhtml = function(s) {
	return s.split("&").join("&").split("<").join("<").split(">").join(">");
}
js.Boot.__trace = function(v,i) {
	var msg = i != null?i.fileName + ":" + i.lineNumber + ": ":"";
	msg += js.Boot.__string_rec(v,"");
	var d;
	if(typeof(document) != "undefined" && (d = document.getElementById("haxe:trace")) != null) d.innerHTML += js.Boot.__unhtml(msg) + "<br>"; else if(typeof(console) != "undefined" && console.log != null) console.log(msg);
}
js.Boot.__clear_trace = function() {
	var d = document.getElementById("haxe:trace");
	if(d != null) d.innerHTML = "";
}
js.Boot.isClass = function(o) {
	return o.__name__;
}
js.Boot.isEnum = function(e) {
	return e.__ename__;
}
js.Boot.getClass = function(o) {
	return o.__class__;
}
js.Boot.__string_rec = function(o,s) {
	if(o == null) return "null";
	if(s.length >= 5) return "<...>";
	var t = typeof(o);
	if(t == "function" && (o.__name__ || o.__ename__)) t = "object";
	switch(t) {
	case "object":
		if(o instanceof Array) {
			if(o.__enum__) {
				if(o.length == 2) return o[0];
				var str = o[0] + "(";
				s += "\t";
				var _g1 = 2, _g = o.length;
				while(_g1 < _g) {
					var i = _g1++;
					if(i != 2) str += "," + js.Boot.__string_rec(o[i],s); else str += js.Boot.__string_rec(o[i],s);
				}
				return str + ")";
			}
			var l = o.length;
			var i;
			var str = "[";
			s += "\t";
			var _g = 0;
			while(_g < l) {
				var i1 = _g++;
				str += (i1 > 0?",":"") + js.Boot.__string_rec(o[i1],s);
			}
			str += "]";
			return str;
		}
		var tostr;
		try {
			tostr = o.toString;
		} catch( e ) {
			return "???";
		}
		if(tostr != null && tostr != Object.toString) {
			var s2 = o.toString();
			if(s2 != "[object Object]") return s2;
		}
		var k = null;
		var str = "{\n";
		s += "\t";
		var hasp = o.hasOwnProperty != null;
		for( var k in o ) { ;
		if(hasp && !o.hasOwnProperty(k)) {
			continue;
		}
		if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") {
			continue;
		}
		if(str.length != 2) str += ", \n";
		str += s + k + " : " + js.Boot.__string_rec(o[k],s);
		}
		s = s.substring(1);
		str += "\n" + s + "}";
		return str;
	case "function":
		return "<function>";
	case "string":
		return o;
	default:
		return String(o);
	}
}
js.Boot.__interfLoop = function(cc,cl) {
	if(cc == null) return false;
	if(cc == cl) return true;
	var intf = cc.__interfaces__;
	if(intf != null) {
		var _g1 = 0, _g = intf.length;
		while(_g1 < _g) {
			var i = _g1++;
			var i1 = intf[i];
			if(i1 == cl || js.Boot.__interfLoop(i1,cl)) return true;
		}
	}
	return js.Boot.__interfLoop(cc.__super__,cl);
}
js.Boot.__instanceof = function(o,cl) {
	try {
		if(o instanceof cl) {
			if(cl == Array) return o.__enum__ == null;
			return true;
		}
		if(js.Boot.__interfLoop(o.__class__,cl)) return true;
	} catch( e ) {
		if(cl == null) return false;
	}
	switch(cl) {
	case Int:
		return Math.ceil(o%2147483648.0) === o;
	case Float:
		return typeof(o) == "number";
	case Bool:
		return o === true || o === false;
	case String:
		return typeof(o) == "string";
	case Dynamic:
		return true;
	default:
		if(o == null) return false;
		if(cl == Class && o.__name__ != null) return true; else null;
		if(cl == Enum && o.__ename__ != null) return true; else null;
		return o.__enum__ == cl;
	}
}
js.Boot.__cast = function(o,t) {
	if(js.Boot.__instanceof(o,t)) return o; else throw "Cannot cast " + Std.string(o) + " to " + Std.string(t);
}
js.Lib = function() { }
js.Lib.__name__ = true;
js.Lib.debug = function() {
	debugger;
}
js.Lib.alert = function(v) {
	alert(js.Boot.__string_rec(v,""));
}
js.Lib["eval"] = function(code) {
	return eval(code);
}
js.Lib.setErrorHandler = function(f) {
	js.Lib.onerror = f;
}
if(Array.prototype.indexOf) HxOverrides.remove = function(a,o) {
	var i = a.indexOf(o);
	if(i == -1) return false;
	a.splice(i,1);
	return true;
}; else null;
Math.__name__ = ["Math"];
Math.NaN = Number.NaN;
Math.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Math.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Math.isFinite = function(i) {
	return isFinite(i);
};
Math.isNaN = function(i) {
	return isNaN(i);
};
String.prototype.__class__ = String;
String.__name__ = true;
Array.prototype.__class__ = Array;
Array.__name__ = true;
Date.prototype.__class__ = Date;
Date.__name__ = ["Date"];
var Int = { __name__ : ["Int"]};
var Dynamic = { __name__ : ["Dynamic"]};
var Float = Number;
Float.__name__ = ["Float"];
var Bool = Boolean;
Bool.__ename__ = ["Bool"];
var Class = { __name__ : ["Class"]};
var Enum = { };
var Void = { __ename__ : ["Void"]};
if(typeof document != "undefined") js.Lib.document = document;
if(typeof window != "undefined") {
	js.Lib.window = window;
	js.Lib.window.onerror = function(msg,url,line) {
		var f = js.Lib.onerror;
		if(f == null) return false;
		return f(msg,[url + ":" + line]);
	};
}
HelloWorld.main();
})();
</function>```


<p>ふむふむ、なにやら型周りの判定コードなんかが見られますが、読めないレベルではないですね。なお、標準でStdモジュールはimportされる、とのことです。

おそらく実際はここからminifyして使うでしょうし。ここまで大体15分くらいでできました。</p>

<h2>IDEを使いたい!</h2>
<p>チュートリアルによると、「FlashDevelop」というIDEが一番良い、とされてますが、残念ながらMacでは動かず。で、ちょろちょろと探した結果、MonoDevelopというIDEにはHaxeバインディングが用意されているらしいので、それを入れてみました。下記のサイト参照です。</p>

<blockquote><p><a href="http://www.matthijskamstra.nl/blog/index.php/2012/06/21/haxe-ide-for-osx-monodevelop-3-0/" target="_blank">Haxe IDE for OSX: Monodevelop 3.0 | [mck]</a></p></blockquote>

<p>まずはMonodevelopのダウンロードから。

<a href="http://monodevelop.com/Download" target="_blank">MonoDevelopダウンロードページ</a>

ダウンロードページからOSを選択してDLしますが、先に「Mono + GTK#」というパッケージを入れないと、IDEがlaunchできないよって怒られますので、先にインストールしましょう。起動したら大体日本語されてました。すごい。

続いて、HaxeバインディングもAdd-inリポジトリからダウンロードして、インストールします。mpackというメッセージパック?ファイルがDLされる模様。これもMacのパッケージを選択。

<a href="http://addins.monodevelop.com/Project/Index/41" target="_blank">Haxe Language Binding</a>

あとは「Monodevelop」→「アドインマネージャ」と進んで、左下の「Install from file...」で、DLしたバインディングファイルを指定すればインストール完了でした。簡単ですね。</p>

<p><img src="/media/d92f51cbdccf588c8d447a0c178ec12f.png"></p>


<h2>こっちでもHelloWorld</h2>
<p>MonoDevelopからHelloWorldを出してみます。

MonoDevelopでは、プロジェクトのもう一階層上というか、「ソリューション」というものがあるみたいで、まずはそれを作らないといけないようです。「ファイル」→「新規」→「ソリューション」で作ります。</p><p><img src="/media/510363446417b7133bf954a945b4941f.png"></p><p><br></p><p> で、ファイルツリーが見えない&展開できないので、「ビュー」→「デフォルト」で左の「ソリューション」をアクティブにするとツリーが見えました。なんだこれ。

<img src="/media/870640de5ecd2adf8a5dc9815a5b52e9.png"></p>

<h3>ビルド設定とサンプルコード</h3>
<p>IDEでは、hxmlファイルをもとにビルドするようなので、このファイルを見てみます。HelloWorld.hxmlです。</p>

-cp Source -main Helloworld -js Export/Helloworld.js

<p>要はターミナルでコンパイルするときのオプションが書かれてるだけのようです。--js-modernを追加しとくといいみたいですね。</p>

<ul>
<li>Source : Haxeのソースコード設置先</li>
<li>Export : コンパイル後のJSファイルとHTMLが設置される</li>
</ul>

<p>というファイル構成のようです。なお、コンパイルされたJSファイルはデフォルトではMonoDevelopでは見えないので、プロジェクトを右クリック→「オプションを表示」→「全てのファイルを表示」にチェックを入れとくといいと思います。

デフォルトで作られる雛形はこんな感じでした。Source/Helloworld.hx</p>

package;

/**

  • @author sugimoto */ class Helloworld {

    public function new () { }

    static function main () { new Helloworld (); }

}

<p>newするタイプの雛形ですね。ではダイアログを出すように書いてみます。</p>

package;

import js.Lib; /**

  • @author sugimoto */ class Helloworld {

    public function new () { js.Lib.alert(‘Hello World’); }

    static function main () { new Helloworld (); }

}

<p>あとはビルドするだけです。ちなみに、js.Libはimportしなくても動きました。自動なのかな?</p>

<h3>ビルドして表示してみる</h3>
<p>ソリューションツリーのプロジェクトの部分を右クリックしてビルドします。もしくは、ツールバーのブロックみたいなアイコンでもビルド開始できますね。成功したら、ステータスバーに「ビルド成功」と出て、Export内にHelloworld.jsが出来上がっているはずです。

<img src="/media/181936cf5b66db7c7f9d90be855e6801.png">

あとは、同じくプロジェクトを右クリックし、「Start  Debugging Item」を選択すると、Chromeが立ち上がって(多分デフォルトブラウザが起動します)、ダイアログが出ます。

<img src="/media/7335891987a169f7a3d9eead0c7580cd.png">

これでひとまず導入まではいけました!</p>

<h2>感想など</h2>
<p>公式ドキュメントもまだちゃんと読んでないですが、コード補完はそこそこ出てるんじゃないでしょうか?IDEは好みもあるので何ともですが、ファイルツリー以外はまぁ使える感じです。もっと深く勉強すると色々足りない所が出てきそうですが…。パッケージ使ったり、それほど規模の大きいスクリプトでなければターミナルからお手軽にコンパイルできるので、そういう使い方でいいんじゃないかなー。

とりあえず導入まで出来たので、何かサンプルを作りながら勉強していこうかなーと思っています。
※ちなみにSublime Text2のHaxeパッケージもやってみたのですがしっくりこなかったので割愛(;´Д`)

</p>