LoginSignup
4
0

More than 3 years have passed since last update.

Windowsでescriptするときにちょっぴり幸せになれる Tips

Posted at

1.escript ってなに?

ご存じ赤丸急上昇中の Elixirと言えば、WEBフレームワークの Phoenix、インタラクティブUIの LiveView、そして異色のIOTシステム Nervesと、比較的規模の大きなガチな面々が有名で、ライトな用途には向かないよーな印象がある……少なくとも私にはそう見えていた:sweat_smile:

ところがどっこい、Elixirでもライトな CLI(Command Line Interface)アプリを作ることが出来るのだ。後ほど詳しく触れるが、Elixirプロジェクトのディレクトリで、

mix escript.build

とすれば、CLIアプリのコマンドが作成できる(以下escriptと呼ぶ)。もちろん、shellで起動すればちゃんと動く。

そう、ここまでは良かった。だが、この escriptは Windowsのコマンドプロンプトでは実行できないのだ。残念、Windowsファンは指を咥えて我慢するしかないのか?:disappointed_relieved:

2.escriptを実行するカラクリ

できないとなると、俄然何とかならないものかと悪あがきをしたくなるのが、天邪鬼な技術屋の性分である。

取り敢えず、escriptを実行するカラクリを調べてみよう。えいやっと、escript - helloとする - のバイナリ・ダンプを取ってみると、下図のようになっていた。なるほど、un*xシェルの shebang "#! /usr/bin/env escript"を応用している訳ね[*1]。となれば脈はある:sunglasses:
escript.jpg
[*1]shebang: un*xシェルは、<ファイル>の先頭に "#! cmd"のパターンを見つけると、代わりに "cmd <ファイル>"を実行する。上の helloでは、実際には "/usr/bin/env escript hello"が実行される。

3."mix escript.build"にバッチファイルを作らせる

では始めよう。

最初に、いつもの通りElixirプロジェクトを用意しよう。
プロジェクト名は "hello"、実行するとコンソールに"Hello world!"と表示する escriptを作る。

C:\home\Elixir>mix new hello
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/hello.ex
* creating test
* creating test/test_helper.exs
* creating test/hello_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd hello
    mix test

Run "mix help" for more commands.

escriptのプロジェクトでは、コマンドのエントリーポイントとなる "main(args \ [])"関数を用意する必要がある(下記)。

hello.ex
defmodule Hello do
  @moduledoc """
  Documentation for `Hello`.
  """

  @doc """
  Hello world.
  """
  def main(args \\ []) do
    IO.puts "Hello world!"
  end
end

そして、mix.exsの project()に 属性escriptを追加し、コマンド・エントリーポイントのモジュール"Hello"を指定する。

mix.exs(抜粋)
  def project do
    [
      app: :hello,
      version: "0.1.0",
      elixir: "~> 1.10",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      escript: escript()
    ]
  end

  defp escript do
    app_name = "hello"
    [
      main_module: Hello,
    ]
  end

ここまでが、普通の escriptを作成するための設定だ。

ここからひと捻りして、Windowsのバッチファイルが作られるように改造しよう。
属性escriptには、main_moduleの他に shebangの文字列(:shebang)や escriptファイル名(:path)を指定できるサブ属性がある。これらを利用するのだ。

サブ属性pathは、作成されるescriptファイル名が "hello.bat"となるように設定する。

一方、サブ属性shebangには下記の文字列を設定する。これが手品の種だ。
"%~f0"の部分はバッチファイルのコマンド引数表記で、自分自身のバッチファイル名の絶対PATHに置き換わる表記だ。したがって本例では、Windowsのコマンドプロンプトによって「#! escript "C:\home\Elixir\hello\hello.bat"」と解釈され、そして実行されるのだ。その通り、コマンド "#!" に引数 "escript"と "C:\home\Elixir\hello\hello.bat"を与えて実行せよだ:wink:

shebang: "#! escript \"%~f0\"\n"

そして、もう一つの手品の種がこれ。中身が下記で名前が "#!"のバッチファイルを作成し、PATHが通ったディレクトリに置いておく。

#!.bat
@echo off
%*

結果、mix escript.buildで作成された hello.batを実行すると、下記の様にバッチファイルの連鎖が起こり、無事escriptが実行されるのだ。めでたしめでたし。

hello.bat
=> #! escript "C:\home\Elixir\hello\hello.bat"
=> escript "C:\home\Elixir\hello\hello.bat"

mix.exsの修正箇所をまとめると下記の通り。

mix.exs(修正)
  defp escript do
    app_name = "hello"
    [
      main_module: Hello,
      shebang: "#! escript \"%~f0\"\n",
      path: "#{app_name}.bat"
    ]
  end

では、試しにやってみる。
ほぼ期待通りだ。途中で echo出力(下から2行目)が出てしまうのはご愛嬌かな:sweat_smile:

C:\home\Elixir\hello>mix escript.build
Compiling 1 file (.ex)
warning: variable "args" is unused (if the variable is not meant to be used, prefix it with an underscore)
  lib/hello.ex:15: Hello.main/1

Generated hello app
Generated escript hello.bat with MIX_ENV=dev

C:\home\shozo\Elixir\hello>hello.bat

C:\home\shozo\Elixir\hello>#! escript "C:\home\shozo\Elixir\hello\hello.bat"
Hello world!

4.エピローグ

「あきらめない、あきらめない、あきらめない ‥‥」

Windowsでも気楽に escriptを書いてみようかなという気持ちが湧いてきた。
ちょっぴり幸せになったかも:blush:

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0