git-nowをhomebrew本家にpull-requestした、それFormulaについてあれ

たまゆらにでてくるスイーツが毎回おいそうで固唾を飲んでいる今日このごろです。

id:sinsokuさんに許可をいただいて、サブコマンドほげほげしたgit-nowをhomebrewに追加すべくpull-requestを送りました。

https://github.com/mxcl/homebrew/pull/8169

それで当然Formulaを書いたんですが、色々try and errorがあったのでここに書きます。

Formula自体は最終的に以下のようにしました。*1

require 'formula'

class GitNow < Formula
  url 'https://github.com/iwata/git-now.git', :tag => 'v0.1.0.6'
  version '0.1.0.6'
  head 'https://github.com/iwata/git-now.git', :branch => 'develop'
  homepage 'https://github.com/iwata/git-now'

  def options
    [
        ['--gnu-getopt', "Link a gnu-getopt formula"],
        ['--zsh-completion', "copy zsh completion function file to $fpath"]
    ]
  end

  # for longopt
  if ARGV.include? '--gnu-getopt'
    depends_on 'gnu-getopt'
  end

  def install
    system "make", "prefix=#{prefix}", "install"
    if ARGV.include? '--gnu-getopt'
      system "brew ln gnu-getopt"
    end
    if ARGV.include?('--zsh-completion') && ENV.include?('FPATH')
      ENV['FPATH'].split(':').find do |path|
        system "cp etc/_git-now #{path}" if File.directory? path
      end
    end
  end

end

それで以前からも色々迷っていたのがoptionの部分。

Formulaではoptionsメソッドでbrew optionsしたときに表示されるinstall optionのリストを記述できます。あと、そのoptionが指定されているかどうかはARGV.include?で確認できます。

git-nowでは--gnu-getoptと--zsh-completion optionをつけました。

--gnu-getopt

gnu-getoptは以前書いたようにshflagsでlong-optionを使うために必要なものです。

homebrewのgnu-getoptはkeg-onlyなんでdepends_onしただけでは有効にならず、brew link gnu-getoptしないといけないんですがdepends_onと違い、brew linkをFormulaでうまい具合にやる方法がありません。

できる方法とすればsystemメソッドでbrew linkしてやるしかないのですが、それをやってしまうとupgradeとかしたときにも毎回brew linkが走ってエラーになってしまいます。

なので、--gnu-getoptを指定したときだけbrew linkするようにして解決(?)しました。つまり、最初の一回だけ--gnu-getoptすればいいわけですね!

--zsh-completion

git-nowのzsh補完関数をがんばって書きました><(その話はまた別のエントリーで)

なので、brewで補完関数もいれてくれたらいいなぁと思って色々苦心しました…

bashの場合は/usr/local/etc/bash_completion.dができて、Formulaの中ではetcプロバティでこのpathを参照できて、cpしてやればいいわけです。

zshの場合はデフォルトでは/usr/local以下に補完関数のディレクトリは存在しません。zshの補完関数ファイルは$fpathというディレクトリにおくことになっています。

例えばechoで$fpathをみると、

$ echo $fpath
/usr/share/zsh/site-functions
/usr/local/Cellar/zsh/4.3.12/share/zsh/site-functions
/usr/local/Cellar/zsh/4.3.12/share/zsh/functions

このいずれかに補完関数ファイルをcpすればよいということになります。なのでFomulaでは--zsh-completion optionがついたらcpするようにしてます。それで$fpathをFormulaで取得するんですが、ruby 7級のぼくにはよく分かりません><ENV[key]で環境変数を参照できることは分かったんで、ENV['fpath']を試してみると入ってない…orz

zsh fpathでググってみると、どうやらzshrcとかでexport FPATHしないとだめらしい。export書いてENV['FPATH']すると、rubyでFPATHをとれました。FPATHの場合はfpathと違ってコロン(:)区切りなんで、splitして最初にdirectoryの存在が確認されたやつにcpしてます。

exportしてないとFPATHが参照できないため、--zsh-completionしてあってもENV.include?して参照できる場合だけcpしてます。

ちなみに僕は$HOME以下のpathをfpathに追加してるので、そっちにcpされます。

とまあ結構長文になったんですが、まだpull-requestへのレスはきてない*2のでhomebrew install git-nowとかできないんですが、とりこまれたらバージョン管理とかも楽になるかなぁと。といいつつ特にバージョンアップの予定はないんですけどね><

追記 2011/10/21

pull-requestにレスが返ってきたんですが、--gnu-getoptのときにbrew lnしてるところがダメらしくてrejectされました><

じゃあどうすりゃいいんだろ…まあoptionにしてるからいいんじゃないかと思うんですが…

追記2 2011/10/22

zshのFormulaをみるとcompletion functionsディレクトリが#{share}/zsh/functionsになっていたので、--zsh-completionでの動作を以下のように修正しました。

@@ -17,16 +17,18 @@ class GitNow < Formula
   if ARGV.include? '--gnu-getopt'
     depends_on 'gnu-getopt'
   end
+  if ARGV.include? '--zsh-completion'
+    depends_on 'zsh'
+  end
 
   def install
     system "make", "prefix=#{prefix}", "install"
     if ARGV.include? '--gnu-getopt'
       system "brew ln gnu-getopt"
     end
-    if ARGV.include?('--zsh-completion') && ENV.include?('FPATH')
-      ENV['FPATH'].split(':').find do |path|
-        system "cp etc/_git-now #{path}" if File.directory? path
-      end
+    if ARGV.include?('--zsh-completion')
+      zsh_functions_d = share + 'zsh/functions'
+      zsh_functions_d.install "etc/_git-now"
     end
   end

install使っているので、brew removeしたときに一緒にこの補完関数も削除してくれます。

ただしzshrcとかでfpathに#{share}/zsh/functions*3を追加しないといけません。

追記3 2011/10/27

最終的なformulaはhttp://d.hatena.ne.jp/mobcov/20111027/1319721530に書いたのでそちらを参照のこと。

*1:更新したので差分は追記2を参照

*2:レスはきたけどrejectされた…orz

*3:デフォルトでは/usr/local/share/zsh/functions