LuaRocks で Lua のモジュールを管理する

  • 39
    Like
  • 0
    Comment
More than 1 year has passed since last update.

lua のコードをあちこちに書き散らかすよりも、機能毎にモジュール化するほうが何かと便利なので lua のパッケージマネージャの一つ LuaRocks(ちなみに、issue を見ると LR って略されてるね)を利用してモジュール化してるんだけど、今回はそのメモ。

まぁ、基本的なことはほぼドキュメント - http://luarocks.org/en/Documentation - に書いてあるし、チュートリアル見たらわかると思うので割愛して、独自リポジトリでのモジュール管理について。

っても、ドキュメントが結構残念な感じに不明な点がチラホラあるんだけど、マイナー言語なので仕方ないのかも。。

とりあえず LuaRocks をインストール

いつの間にか LuaRocks のバージョンが上がっていたので最新版をインストールする。インストールは手動でもいいけれど簡単なので Homebrew でインストール。

brew install luarocks

それから、基本的に Mac を管理者権限でログインして使うことがないのでホームディレクトリに ~/.luarocks ディレクトリを作っておく。

mkdir ~/.luarocks

一般ユーザー権限でモジュールをインストールする時はこのディレクトリが使われることになる。あと、この中に config.lua という設定用の lua ファイルも置けるけど今回は何もしないので詳しく知りたい人はドキュメントを読むと良いよ。

Note: 普段から管理者権限で動かしてる人は気にしないでおk。

モジュールのインストール方法色々

LuaRocks はモジュール化する時に便利だしデファクトっぽいしって理由で を利用しているのだけれど、作ったモジュールをデフォルトのリポジトリには公開してない。

なんかね、lua いじりだしてまだ1年半くらいでマナーがいまいちよくわからないってのもあるし、npm でもそうだけどモジュール名がかぶるのを気にして変な名前にするのもなんかなんかだしなぁ・・・とか、etc. な理由からなんだけど、でも、このままではものすごく不便でしょうがない。

例えば、普通に luarocks コマンドを使ってモジュールをインストールするときは;

luarocks --local install <module_name>

Note: 一般ユーザーなので --local オプションを付けて ~/.luarocks にインストールされるように指定しているけど、管理者権限ならいらない。

とすると、デフォルト公開リポジトリからダウンロードされてインストールされる。もちろん検索する時は;

luarocks search <module_name>

とすることでデフォルト公開リポジトリに上がってるモジュールが検索される。

ちなみに、インストールしたモジュールを OpenResty 使いたい時は luarocks pathLUA_PATHLUA_CPATH が表示されるので、これを lua_package_pathlua_package_cpath に追加してあげればおk。

次に自分で作ったモジュールのインストール方法として、例えば以下のような hello モジュールの rockspec ファイル hello-scm-1.rockspec があるとする;

─ lua-hello
  ├─ hello-scm-1.rockspec
  ├─ hello.lua
  └─ lib
     └─ world.lua
hello.lua
local function hello()
    print( 'hello' );
end

return hello;
lib/world.lua
local function world()
    print( 'hello world' );
end

return world;
hello-scm-1.rockspec
package = "hello"
version = "scm-1"
source = {
    url = "git://example.com/mah0x211/lua-hello.git"
}
description = {
    summary = "example hello module",
    detailed = [[example hello module]],
    homepage = "https://example.com/mah0x211/lua-hello", 
    license = "MIT/X11",
    maintainer = "mah0x211"
}
dependencies = {
    "lua >= 5.1"
}
build = {
    type = "builtin",
    modules = {
        hello = "hello.lua",
        ["hello.world"] = "lib/world.lua"
    }
}

これがローカルにある時はディレクトリ内で;

luarocks --local make

でインストールできる。リモートのどこかにある時は rockspec の URL を指定して;

luarocks --local install https://example.com/mah0x211/lua-hello/hello-scm-1.rockspec

なんてやると rockspec 内に書いてある source.url からダウンロードしてインストールされる。でも、これってめんどくさいし、例えば以下のように;

dependencies = {
    "lua >= 5.1",
    "my_dep_module"
}

my_dep_module が自分で作ったモジュールで、それに依存してるとデフォルト公開リポジトリから検索されるので上手くいかない。npm みたいに URL 指定できればいいんだけどね。こまた。

GitHub のリポジトリでモジュール管理

ドキュメントがあっさりしすぎてて肝心なトコがよくわからなかったのでずっと放置して、手動インストールをしてたんだけど、さすがにそろそろなんとかしたい。そんなわけで、ふと GitHub で自分用のリポジトリを作って管理できたら良いかもって思いついたのでやってみた。

ドキュメントによると、LuaRocks をインストールすると luarocks-admin というツールも一緒にインストールされる。これを使って manifest ファイルというリポジトリのインデックスファイルを作って自分の好きな場所に上げて管理できるとのことだったので早速やってみた。

まずは適当に GitHub でリポジトリを作って clone してくる。今回は自分のアカウントで rocks というリポジトリを作ってみた。

git clone https://github.com/mah0x211/rocks.git
cd rocks

この中に管理したいモジュールの rockspec ファイルをコピーして luarocks-admin make_manifestmanifest ファイルを生成する。とりあえず、自作のテンプレートエンジン tsukuyomi の rockspec をいれてみる。

cp /path/to/tsukuyomi/tsukuyomi-scm-1.rockspec ./
luarocks-admin make_manifest .

こうすると以下のファイルが生成された。

  • index.html
  • manifest
  • manifest-5.1
  • manifest-5.2

これをコミット&プッシュして GitHub に上がったところで luarocks から利用してみる。デフォルト以外のリポジトリは --from=<server> オプションで指定することで優先して見にいかせられるとヘルプに書いてあったのでやってみる。詳しくは luarocks help で。

luarocks --from=https://github.com/mah0x211/rocks/raw/master/ search tsukuyomi

Warning: Failed searching manifest: Failed loading manifest: Failed extracting manifest file

Search results:
===============

おー! うまくいかない。 おかしい。。

色々ぐぐってみるとヘルプには書かれてない --verbose オプションがあるようなので指定してもう一度やってみて確認する。

... curl -L --user-agent 'LuaRocks/2.1.2 macosx-x86 via curl' 'http://mah0x211.github.io/rocks/manifest-5.1.zip' 2> /dev/null 1> ...

ファッ!?なんで zip なの?調べてみると Release history にそれっぽいことが書かれている。

Version 2.1.1 - 29/Oct/2013 - download

Remote manifests are now compressed and locally cached, making commands faster

ということは、未実装かまたしてもドキュメント化してないオプションでもあるくさいのでソースを調べてみる。

cat `which luarocks-admin`
#!/usr/bin/env lua

local loader = require("luarocks.loader")
local command_line = require("luarocks.command_line")

program_description = "LuaRocks repository administration interface"

commands = {
   help = "luarocks.help",
   make_manifest = "luarocks.make_manifest",
   add = "luarocks.add",
   remove = "luarocks.admin_remove",
   refresh_cache = "luarocks.refresh_cache",
}

command_line.run_command(...)

luarocks.make_manifestrequire する時のパス表記になってるからモジュールっぽい。/usr/local/share/lua/5.1/luarocks/make_manifest.lua の中を覗いてみる。

cat /usr/local/share/lua/5.1/luarocks/make_manifest.lua 

--- Module implementing the luarocks-admin "make_manifest" command.
-- Compile a manifest file for a repository.
module("luarocks.make_manifest", package.seeall)

local manif = require("luarocks.manif")
local index = require("luarocks.index")
local cfg = require("luarocks.cfg")
local util = require("luarocks.util")
local deps = require("luarocks.deps")
local fs = require("luarocks.fs")
local dir = require("luarocks.dir")

help_summary = "Compile a manifest file for a repository."

help = [[
<argument>, if given, is a local repository pathname.

--local-tree  If given, do not write versioned versions of the manifest file.
              Use this when rebuilding the manifest of a local rocks tree.
]]

--- Driver function for "make_manifest" command.
-- @param repo string or nil: Pathname of a local repository. If not given,
-- the default local repository configured as cfg.rocks_dir is used.
-- @return boolean or (nil, string): True if manifest was generated,
-- or nil and an error message.
function run(...)
   local flags, repo = util.parse_flags(...)

   assert(type(repo) == "string" or not repo)
   repo = repo or cfg.rocks_dir

   util.printout("Making manifest for "..repo)

   if repo:match("/lib/luarocks") and not flags["local-tree"] then
      util.warning("This looks like a local rocks tree, but you did not pass --local-tree.")
   end

   local ok, err = manif.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"])
   if ok and not flags["local-tree"] then
      util.printout("Generating index.html for "..repo)
      index.make_index(repo)
   end
   if flags["local-tree"] then
      for luaver in util.lua_versions() do
         fs.delete(dir.path(repo, "manifest-"..luaver))
      end
   end
   return ok, err
end

manif.make_manifest って箇所がきっとそれだってことで覗いてみると zip_manifests という関数が定義されてるからこれを呼んでるトコを find /usr/local/share/lua/5.1/luarocks | xargs grep zip_manifests で探してみると /usr/local/share/lua/5.1/luarocks/add.lua からしか呼ばれてない。。

これって luarocks-admin add でリモートリポジトリにアップロードするコマンドオプションなはずなので使えないっぽいし。。ここからしか呼ばれてないからこれ以上調べてもしょうがないし、手動で zip するとバージョン番号の付与とかめんどうなので、luarocks のモジュールを直接使うコードを書いて対応する。

makeZip.lua
local cfg = require("luarocks.cfg")
local manif = require("luarocks.manif");

manif.zip_manifests();

これを実行して manifest-5.1.zipmanifest-5.2.zip が作られてコミット&プッシュして確認してみると。

luarocks --from=https://github.com/mah0x211/rocks/raw/master/ search tsukuyomi

Search results:
===============

Rockspecs and source rocks:
---------------------------
tsukuyomi
   scm-1 (rockspec) - https://github.com/mah0x211/rocks/raw/master

できた。疲れた。。午前中まるっとついやしたし。。

疲れたけど index.html も生成されてるし、せっかくだから gh-pages ブランチ作って http://mah0x211.github.io/rocks/ にアクセス出来るとわかりやすいだろなってことで、もうちょっとちょこちょこいじって完成。

生成された index.html のデザインが残念すぎるけど、とりあえずこれでいいや。