大学生からの Web 開発

会社の人に見つかってぽよぽよしてきた

gem の実行可能ファイルは exe に保存する

作った gem をビルドしていざ実行しようとしても、 Command not found が返された。解決法が分かったのでシェア。

Bundler で作っている。バージョンは1.8.2

作った gem の構成

├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── sample-gem.gemspec
├── bin
│   ├── sample-gem
│   ├── console
│   └── setup
├── lib
│   ├── sample-gem
│   │   ├── arguments.rb
│   │   ├── client
│   │   │   ├── base.rb
│   │   │   ├── google.rb
│   │   │   └── tumblr.rb
│   │   ├── command.rb
│   │   ├── command_builder.rb
│   │   ├── image.rb
│   │   └── version.rb
│   └── sample-gem.rb
└── pkg
    └──sample-gem-0.0.1.gem

sample-gem という名前の gem で、実行ファイルは慣習に沿ってbinディレクトリに gem と同名で作成している。

あなたの実行ファイルは bin に設置するべき設定ですか

sample-gem.gemspec を見よう。ここに実行できなかった原因はある。

  ......
  spec.license       = "MIT"

  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.8"
  ......

注目してほしいのは spec.bindirspec.exectablesである。この設定では、exeディレクトリ以下にあるファイルをすべて実行ファイルとして認識する。これは Bundler v1.8.2ではデフォルトで生成される gemspec だ。

bundler/newgem.gemspec.tt at master · bundler/bundler

作った gem は binディレクトリ に実行ファイルを置いていたため、ここで設定が食い違っているのだ。

というわけで修正

sample-gem.gemspec

......
spec.bindir        = "bin"
spec.executables   = ['sample-gem']
......

spec.bindirbin を指定し、spec.executables は実行させたいファイル名を配列で記述する。これで解決した。

なぜデフォルトでは exe を指定しているのか

そのへんの理由はここに書いてある。

move gem bins to exe/ and add console and setup · ab3e217 · bundler/bundler

it's a suggestion for new gems, not a suggestion to move your current binaries. old gems will keep working the way they always have. the reason is that rails, bundler, and the gem itself all want to use bin/ for scripts that are specific to the given repository, and rubygems bins are not usable directly. you could even create a bin/my-command for development if you want, while keeping the command that will ship inside your .gem file in exe/.

rubygemsbin を見てることはないし、binsetupconsole といった開発の際に使うファイルを置きたい。あなたが作った executables なコマンドは exe で面倒見ようよ。って感じ。spec.executablesを仮に spec.files.grep(%r{^bin/}) { |f| File.basename(f) }bin ディレクトリすべてを見ることにすると setupconsole までコマンドとしてしまうから困った、ならこうしよう。って感じの改変だと思う。

まとめ

実行できない時はgemspecspec.bindir, spec.executbles をチェック。

これからは、実行ファイルは exe に入れるべきですな。