【Crystal】releaseフラグの警告の謎、crystalコマンドの罠
最初に
久しぶりにCrystalを触って詰まって、自分のブログ記事に助けられた。
その2021年9月のブログ記事を改めて編集し、ここにも投稿する。
内容は、下記の内容です。
-
benchmark
ライブラリを使ったときの--release
フラグの警告の謎 - 上記の原因、
crystal
コマンドの説明、run
サブコマンドの有無の挙動の違い等
動作環境
なお、本記事は、Macで最新のCrystal 1.4.1(2022-04-22)で検証。
表記揺れの注意
本記事は、フラグとオプションという言葉の表記が揺れている。
Crystalでオプション(フラグ)の有無はflag?
マクロで確かめたりエラー文で「flag」と書かれたので、フラグという表現も用いているが、一般にはオプションだと思われる。
benchmarkを使うと、謎の警告がでる。
require "benchmark"
Benchmark.bm{}
上記のように必要最小限にbenchmark
ライブラリを使ったコードを実行してみる。
$ crystal benchmark.cr
Warning: benchmarking without the `--release` flag won't yield useful results
なんか警告がでる。読んで見ると、
--release
フラグをつけないベンチマーク計測は、意味のある結果をださない。
という警告がでている。
--release
をつけると、リリースモードを有効にして、生成されるバイナリに最適化をしてコンパイルしてくれるらしい。
最適化してない状態で計測しても意味がないでしょ、リリースモードにして、ということだった。
言われたとおり、コマンドの実行の際に--release
フラグをつけて実行してみる。
$ crystal benchmark.cr --release
Warning: benchmarking without the `--release` flag won't yield useful results
--release
フラグをつけて実行すれば消えるかと思ったが、この警告は消えなかった。
常に出続けるタイプの警告なのだろうか?(結論そういうわけではなかった)
他に、--release
フラグの位置をかえてファイル名より先にすると、
存在しないコマンド扱いされてエラーとなった。
$ crystal --release benchmark.cr
Error: unknown command: --release
なお、Rubyだと、ruby
コマンドとファイル名の間にオプションを書いて動く。
ruby
コマンドについては、最後の方で参考に記述する。
解決方法: runサブコマンドをつける
解決方法を先にいうと、下記のようにrun
サブコマンドをつけて実行すると、警告は消え正しく実行される。
$ crystal run benchmark.cr --release
$ crystal run --release benchmark.cr
--release
オプションの位置は、どちらでもいいみたい。
このあとの記事では、crystalコマンドの挙動等について調べたことを書く。
最初にcrystalコマンドの説明を見てみる
多くのコマンドがそうであるようにcrysltal --help
とすれば、
crystal
コマンドの簡単な説明を得ることができる。
crystal -h
やcrystal help
でもよい。
$ crystal --help
Usage: crystal [command] [switches] [program file] [--] [arguments]
Command:
init generate a new project
build build an executable
docs generate documentation
env print Crystal environment information
eval eval code from args or standard input
i/interactive starts interactive Crystal
play starts Crystal playground server
run (default) build and run program
spec build and run specs (in spec directory)
tool run a tool
help, --help, -h show this help
version, --version, -v show version
Run a command followed by --help to see command specific information, ex:
crystal <command> --help
これを見ると、それぞれのコマンドの説明が見れる。
「run (default)」と書いてあり、サブコマンドを省略するとrun
が適用されるようにみえる。
しかし、実際には省略するとオプションの扱いが変わり、全く同じではない。罠だと思う。
なお、このhelpの説明を見ると、「crystal <command> --help」とあり、
crystal run --help
の形でサブコマンドのもう少し詳しい説明を読むことができる。
コンパイラの使い方 - Crystal
なお、日本で詳しい説明を見たければ、上記のページを見ればよさそう。
runサブコマンドの有無の違い
$ crystal run foo.cr --release
$ crystal foo.cr --release
自分は、上と下が同じに違いないと思った。しかし、実際には違った。
上のrun
コマンドで呼び出した場合は、--release
オプションが機能する。
しかし、下の場合は、--release
オプションが機能しなかった。
これについて、以前CrystalコミュニティのDiscordで雑に尋ねてみたところ、
z64さんという方に「昔はrun
コマンドなしで、--release
オプションが機能した」と教えてもらった。
なぜ削除したのか質問してみたが、正確に覚えてないとのことだった。
ただcrystal foo.cr --release
の場合は、crystal
コマンドのオプションではなく、ファイルのオプションとして認識されるということだった。
{% if flag?(:release) %}
puts ["release flag", ARGV]
{% else %}
puts ["no release flag", ARGV]
{% end %}
上記の内容のファイルを作り、比べてみると分かりやすい。
ここではflag_test.cr
というファイル名にした。
$ crystal run flag_test.cr --release
["release flag", []]
$ crystal flag_test.cr --release
["no release flag", ["--release"]]
run
コマンドの方は、crystal
コマンドのオプションとして--release
が認識されていてflag?
マクロで認識できるがARGV
には残っていない。
対して、run
を省略した場合は、crystal
コマンドのオプションとして認識されずARGV
に残る。
(※ なお、ARGV
というのは、コマンドの引数が入るところ。)
というわけで、上記のような違いがあった。
ところで、rubyコマンドの挙動
ところで、ruby
コマンドの場合はどうなのだろう。
a = 1
p ARGV
例えば、上記のv.rb
で確かめてみる。
$ ruby v.rb
[]
$ ruby v.rb -w
["-w"]
$ ruby -w v.rb
v.rb:1: warning: assigned but unused variable - a
[]
オプションが機能するのは、ruby
コマンドとファイル名の間。
-w
オプション付きでRubyで実行すると、警告が表示されてるのがわかる。
ファイル名の後ろに-w
があると、警告はでず、ARGVに格納されている。
このとき、オプションではなく、コマンド引数として認識されている、のがわかる。
改めて確かめると、ruby
コマンドも位置でオプションかどうか区別されて機能するか決まるので、注意が必要だと感じた。
まとめ
Rubyと違って、Crystalはrun
サブコマンドを書いた方がよさそう。
最初はrun
省略時に何も認識されてないように見え、修正されるべきバグか何かだと思った。
しかし、Crystalコミュニティで質問してみるとファイルのオプション(コマンド引数)として認識されているとのことだった。
初見殺しな感じでわかりにくい気もするが、もしかしたら何か使い分けできて便利かもしれない。
今のところrun
省略時のオプションが機能しないのは非直感的で、何の役の立つのかわかってないので、特に嬉しくはない……。
Discussion