GMake サバイバルガイド
この GMake サバイバルガイドでは、まけふぁいる*1をささっと書いて楽をしたいけど、
どーやって書くのかわからないといった様な人たちに向けてこんな感じに書けば良いんじゃないかなーというのを書いていると思います。
あと、この記事のまけふぁいるのサンプルコードは便宜上 4 半角スペースでインデントしていますが、まけふぁいるのインデントは絶対にタブなので、実際にやって見る場合は注意してください。
最新のソースコードを実行したい!
Crystal のソースコードをコンパイルしてから実行するのは億劫ですよね。
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
何度も続けて行うなら、何とかして自動化してしまいたいもの。
まあ、cyrstal run ./hello.cr
とかすれば Crystal のソースコードから実行できるんですが、それは無視します。
以下のようなへろーわーるどなソースコードがあったとします:
# hello.cr puts "Hello, World!"
make
ってやるだけで、実行ファイルがなかったり古かったりしたらコンパイルして
とりあえず実行したい場合は私はまけふぁいるをこんな風に書きます:
.PHONY : run run: hello ./hello hello: hello.cr crystal build --release $^
.PHONY: run
というのは run
というシンボルはファイルじゃねーからよく覚えとけ!という意味を表明するために書いています。
これは省略可能です。
すぐ下の:
run: hello
./hello
こういうのをこの記事ではレシピ*2と呼びましょう。
レシピとはあるファイル○○を作るにはファイル△△が必要で、××するとできるというのをまけふぁいるの記述で書いたものです。
それをまけファイル用の記述で書くとこのようになります:
○○: △△ ××
先程のレシピに戻ると run
というファイルには hello
というものが必要で、run
を作るには ./hello
が必要で、 hello
コマンドを実行すればいいということが書いてあります。
しかし、run
というファイルは実際には作成されないので、何度も実行することが可能です。
そういえば、hello
はどうやって造られるのでしょう?
hello
のレシピはすぐ下にあります:
hello: hello.cr crystal build --release $^
hello
というファイルには hello.cr
というファイルが必要で、crystal build --release $^
を実行すればいいよね!ということが書いてあります。
$^
は必要なファイル──この場合では hello.cr
ですね──を参照する変数です。
作成するファイルを参照する変数は $@
なので、覚えておいてください。
とはいえ、この 2 つをもし忘れてもレシピは書けます。
hello: hello.cr
crystal build --release hello.cr
という風に書けばいいだけだからです。
替わりにめんどくさいというだけです。
たくさんのソースコードの中で、新しいのだけコンパイルしたい!!
おーけーべいべ!
まさにそのために make
は存在します。
例えば、ソースツリーに以下の LiveScript のソースコードがあったとします:
./routes.ls
./test/index.ls
./gulpfile.ls
./public/components/App.ls
./public/components/AddRoute.ls
./public/components/CounterRoute.ls
./public/store.ls
./public/action.ls
./server.ls
./config.ls
それに対して私の書いたまけふぁいるはこうです:
srcs = \ gulpfile.js \ routes.js \ server.js \ config.js \ test/index.js \ public/store.js \ public/action.js \ public/components/AddRoute.js \ public/components/App.js \ public/components/CounterRoute.js .PHONY : build build: $(srcs) gulpfile.js: gulpfile.ls lsc --debug --no-header -b -c $^ routes.js: routes.ls lsc --debug --no-header -b -c $^ server.js: server.ls lsc --debug --no-header -b -c $^ config.js: config.ls lsc --debug --no-header -b -c $^ test/index.js: test/index.ls lsc --debug --no-header -b -c $^ public/store.js: public/store.ls lsc --debug --no-header -b -c $^ public/action.js: public/action.ls lsc --debug --no-header -b -c $^ public/components/AddRoute.js: public/components/AddRoute.ls lsc --debug --no-header -b -c $^ public/components/App.js: public/components/App.ls lsc --debug --no-header -b -c $^ public/components/CounterRoute.js: public/components/CounterRoute.ls lsc --debug --no-header -b -c $^
これでもまだ力技を使っていますが、わかりやすいかもしれませんね。
test/index.ls
があるので、test
はこのように書けるかもしれません:
test: test/index.ls mocha $^ --compilers ls:livescript
まけふぁいるでは、変数も使えます。
srcs = \ gulpfile.js \ routes.js \ server.js \ config.js \ test/index.js \ public/store.js \ public/action.js \ public/components/AddRoute.js \ public/components/App.js \ public/components/CounterRoute.js
が、その例です。
これは、○○や△△、××で使用できます*3。
変数の値を参照するには $(src)
のように、$(
と )
で変数名を囲みます。
変数を使わない場合、build
はこのように、△△の部分が長くなってしまい、見るに絶えません。
build: gulpfile.js routes.js server.js config.js test/index.js public/store.js public/action.js public/components/AddRoute.js public/components/App.js public/components/CounterRoute.js
そういえば、build
には××の部分が存在しないことに気づいていると思います。
build
の××の部分は、既に△△のそれぞれの××が行っているので、必要ないのです。
(とはいえ、.o
を使って実行ファイルや .dll
、.so
を作成するような場合は必要です。)
今回は .ls
を .js
にトランスパイルするだけなので、××は書いていません。
えーん、あるファイル一つだけ make
したいよー
上の例で server.ls
だけをビルドしたいとしましょう。
この場合は以下のように打つだけです:
% make server.js
public/components/CounterRoute.ls
なら?
% make public/components/CounterRoute.js
ですね。
test/index.ls
なら?
% make test/index.js
ですね。
make CounterRoute.js
や make App.js
としないように気をつけてください。
それらは多分あなたが意図しているのとは違うファイルです。
そうそう、上の例で、make build
と打つ必要はありません。
make
と打つと、一番上のレシピ──この場合は build
──が実行されるからです。
いったんビルドしたのも全部ビルドしたいよ〜
make
にオプション -B
を付けてください。
% make -B
LiveScript でつまずいたこと
関数呼び出し中の do
hoge-func ham, tomato, beacon do a: \foo b: \bar
が:
hogeFunc( ham, tomato, beacon, { a: 'foo', b: 'bar' } );
になって欲しいのだが、時々こういう風になる:
hogeFunc( ham, tomato, beacon({ a: 'foo', b: 'bar' }) );
うん、beacon
はただの変数で、関数じゃねーよwwwwww
もうやだこの altJS…………。
地雷だよね………(´Д⊂グスン
これは、do
を使う前の引数が変数だとなるらしい。
なので、今までやっていた通り:
hoge-func ham, tomato, beacon, a: \foo b: \bar
こんな風に書くことにする。
変数?方式の関数定義は上の方に変数がいっぱい溜まるし、普通に書きたいよー
こーいうのは:
apply-options = (options) !-> heating-power := options.power number-of-people-of-cook := options.number_of_people
こんな風に書ける:
!function apply-options (options) heating-power := options.power number-of-people-of-cook := options.number_of_people
関数を渡す関数内で呼び出し元の this
を使いたい
これは私のじゃっばすくりぷよぢからが 1200 万パワーに満たないために発生したことなんだけど:
class Cook (@skillet) -> cook: (foods) !-> foods.for-each (food) !-> # skillet は Cook インスタンスのものだが、このコンテキスト(for-each に渡された関数)においては # @skillet はないため、あぼーんする @skillet.push food
こういうことをやっていたために、実行すると激しくあぼーんしたり、挙動がおかしくなっていた。
LiveScript の公式サイトを目を皿のようにして眺めたところ、~>
を使うと (・∀・)イイ!! らしいことがわかったので、このように変えた:
cook: (foods) !-> # 見づらいと思うけど、矢印(`->')の横棒(`-')をチルダ(`~')にすると(`~>')、 # lsc がうまく計らってくれる。 foods.for-each (food) !~> @skillet.push food ``
あけおめ!
ʕ•͡ω•ʔ あけおめ
あんまり怖くないと思いたい null
多くの開発者にとって、null
は忌避すべきものだ。
万が一、null
に*さわってしまった*ら、あなたは突如発生した例外により、ヨルダン辺りまで吹き飛ばされるだろう。
あと、えーっと、オフィスの天井も失くなってしまう。
null
の影響で行方不明になったり、命を失ってしまうえんじにゃーは毎年 500 万人を超える*1。
このため、null
をなんとかしようと様々な手法が開発されてきた*2。
つーか、null
を最初にだいにゅーさせなくすればいいんじゃね?
最初にどこかの変数に null
をだいにゅーしておくと、長ーいコードの途中で長い時間をかけて膨らみ、いつしか破裂し、あなたを吹き飛ばす。
これはある。
っていうか、十中八九、これでぬるり*3がでてるんじゃないかと私の中では専らの話題である。
あと、クソ長いコードのせいで、どの時点でどの変数が null
なんだっけ? とか考えるのすっごいめんどい。
Swift)なんかは言語仕様的にできなくしてるみたい。
あんまりないかもしれないけど、メソッドの最初の方に変数宣言をまとめるのやめようぜ?
変数宣言は必要な時に限ってその場で宣言しようぜ!!
( ◠‿◠ ) 変数宣言したけど、その時点ではだいにゅーするもん無いから null
入れとくわーwwwwww
とか、あかんやつだから!!!
null
なんて無い方が良いよね………(過激派)
╰( ´◔ ω ◔ `)╯ まあ、無いなら無い方が嬉しいけどな
頭の良い null
ならいいんじゃないかな
╰( ´◔ ω ◔ `)╯ わりぃ、あんまり思いつかなかったわ
public static NullExtensions { public bool IsNull<_Type>(this _Type self) where _Type : class { return self == null; } }
ʕ•͡ω•ʔ Option か Maybe を返して、何か呼び出す時はその中で行うとか?
あとは……
int.Parse()
じゃなくて、int.TryParse()
使おうなって五億回くらい言ってる。
ファイルのサイズを取得するには?
Strongtalk のソースコードを読んでいたところ、このような記述を見つけた:
void Bootstrap::openFile() { stream_ = fopen( file_name_, "rb" ); if ( stream_ == NULL ) { has_error_ = true; lprintf( "\nCould not open file (%s) for reading!\n", file_name_ ); exit( -1 ); } int no = _fileno( stream_ ); file_size_ = _filelength( no ); }
file_name_
を fopen
関数で開き、_fileno
関数で inode 番号*1を取得し、
それに対応するファイルサイズを _filelength
関数によって取得するのだが、うにっくす環境には _fileno
や _filelength
が無いのである。
C言語でのファイルサイズ取得方法 を見ると、fseek
と SEEK_END
を使うのは脆弱なので、
fstat
を使えと書いてある。
ということで、書いてみた:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> inline off_t get_file_size(const char* filename) { struct stat file_status; stat( filename, &file_status ); return file_status.st_size; }
動かせるようにしたものがこっちに置いてある。
VC++ では stat
の眷属が 12 種類もあることは見なかったことにしておく。
窓に!窓に!
ちなみに github の Strongtalk のコードでは該当箇所はコメントアウトされていた。
*1:と言って良いんだろうか?
うどんこビリーでうどんなお正月!!
╰( ´◔ ω ◔ `)╯ やっぱり C++ も面白いな
カレー・ブルストの作り方
雨に唄えばおじさんと揚げ玉ビーチ
╰( ´◔ ω ◔ `)╯ またまた Swiff の話だ
╰( ´◔ ω ◔ `)╯ 多分な
換気扇の空、オリーブオイルの海、揚げ玉の海岸。
雨に唄えばおじさんは自分が厄介な場所に来てしまったことを悟った。
ということで、以下のコードはコンパイルエラーになる(はず):
let hoge : Nil = nil; // Nil のみの型を持つ変数の宣言はコンパイルエラーになる。 let piyo : int? = nil; // Nil 許容型の変数に nil を初期値として設定するとコンパイルエラーになる。 let fuga : int = nil; // 整数型の変数に nil を初期値として設定するとコンパイルエラーになる。
hoge
の方は、やっても意味がない宣言だと思う。nil
しか設定できないし……。
piyo
の方は、null 排斥運動におけるコンパイルエラー。
多くの場合、最初に null
が入っているから NullReferenceException
が送出されるので、
null
を初期値として設定しないようにすれば多くの場合 NullReferencException
を退けられるだろうという考えらしい。
Wikipedia では「安全性のために」としか書いてないけど。
fuga
の場合はそりゃそーだよなって感じ。int
型の変数に nil
は設定できない。
でも、こっちはどうだろう:
let hage = getHage();
getHage()
がこうなっていると:
def getHage() : int? { if true { return nil; } 42; }
hage
には nil
が設定される。
こういう場合は、「nil
しか返ってこなさそうですよ」って Warning を表示した方が良いんだろうか?
っていうか、こういう場合、Visual Studio*1 では到達不可能なコードなんちゃらあるって言われるよな………。
こういう場合は、やっぱりなんともならないよなぁ:
let hyoga = int.parse( "X" );
まあ、関数の戻り値の nil
はいいことにしようっていうか、リテラルとしての nil
を設定するのがダメってことなんだもんね。
カーネルおじさんによる美味しくできるニワトリ料理パターン
╰( ´◔ ω ◔ `)╯ 毎回記事本体じゃなくて、ナンセンス釣りタイトルを考えるのに時間を費やしてる気がしなくもない
今朝は Swiff の型について考えたような気がする。
自分の中では Swiff も Uva も文法的なところが違うだけのようになってきたので、どんどこどーん!
Swiff は Swift や Rust、Kotlin をインスペクトしているので、let
は変数宣言で、val
は定数宣言になってる。
すごく簡単に書くとこんな感じ:
("let" | "val") <識別子> [":" <型ヒンティング>] "=" <値> ";"
val
だった場合は、<値>
必須だとか色々あるけど、こんな風に変数または定数を定義する。
例を示すとこんな感じ:
let hoge = 42; let piyo : string = "Cunky bacon!!"; val TeddyBearFee = 6000;
上記の例で val
で宣言された識別子、ええっと、定数が UpperCamelCase で表記されていることに気がついた人もいるかもしれない。
そして、定数が UpperCamelCase でないとコンパイラがブーブー言うだろうと思った人は安心していい。
そんなことはない。
Wikipedia の Swift の記事において var
*1 で宣言された識別子は定数と呼ばれていた。
でも、そうじゃなくて、変更できない変数あるいは束縛と呼ぶべきだったのだ。
典型的な関数型プログラミングの考え方では、私たちが変数と呼ばれているものは実は値に名前が書かれた札が紐で結ばれているだけで、
それは分かちがたく結びついているので変更できない。
このことを不変更性を持つと呼ぼう。
札から下がった紐が果てしなくこんがらがって絶対に解くことはできないのだ。
とはいえ、実用的なプログラミング言語にそのようなパズルみたいな難解な代物を持ち込むべきではないと今でも思っている人たちも大勢いるわけで、 彼らにも配慮してやらなければならない。
私としては不変更性はすごく重要だと思っているので Uva では普通に宣言した変数は不変更性を持つようになっている。
どうしても変更可能な変数を宣言したい場合は variable
アノテーションをつければいい。
Swiff はカジュアルな言語を志向しているので、型ヒンティングは :
(コロン)で識別子の後ろに付けることになる。
もちろん、そんな面倒なことをしなくても、型推論によってある程度変数の型はコンパイル時に決定される。
Uva でも型推論で同じように変数の型は決定されるけど、そうしたい旨をコンパイラに伝えないといけない。
それが auto
キーワードだ。
Uva は Swiff の後置に対して前置タイプなので、型ヒンティングを必ず付ける必要が生じる*2。
でも、Uva には静的型付き言語の顔をしてほしかったため、C++ から auto
を取ってきた。
(。ŏ﹏ŏ) こうしてみると、Uva と Swiff って性格が違うんだね
╰( ´◔ ω ◔ `)╯ そうだな
更に、関数定義でも fun
ではなく、def
を採用した。
def is_nil(x : T?) : bool { match x { is T => true, is Nil => false } }
あと、match
式 (・∀・)イイ!!
という感じだったので、switch
は無し。
return
もあるけど、基本的には最後の値が返されるという感じ。
後置 if
とか 後置 unless
や while
、until
もあるよ。
let x = if x < Screen.width then x else Screen.width; // :? タイプの if 式 let y = if y < Screen.height { y } else { Screen.height }; // if っぽい if 式
if
は式だし、then 節はかっこつけなくてもいいんじゃないかとかとかとか…………。