きりかノート 3冊め

おあそびプログラミング

buildbotが作成する、バイナリインストール用gemのファイル配置がおかしいのを直した

いやー、けっこう手間取ってしまった。長く苦しい戦いだった……

潜在的にはすごく前からの問題だったけれど、先日にportgroupを使ったgemのインストールを改善した(r112446)際に影響を受けやすくなってしまった。

現象

件の変更(10/23)移行にbuildbotが作成した、gemのportの配置がおかしくなっている。たぶん対象は3件。

バイナリインストール。

   % sudo port -n upgrade --force rb-pkg-config
     :
   % port contents rb-pkg-config
   Port rb-pkg-config contains:
     /opt/local/lib/ruby/gems/cache/pkg-config-1.1.4.gem
     /opt/local/lib/ruby/gems/doc/pkg-config-1.1.4/rdoc/classes/PKGConfig.html
     :

ソースから手元でインストール(-s)。

   % sudo port -ns upgrade --force rb-pkg-config
   % port contents rb-pkg-config
   Port rb-pkg-config contains:
     /opt/local/lib/ruby/gems/1.8/cache/pkg-config-1.1.4.gem
     /opt/local/lib/ruby/gems/1.8/doc/pkg-config-1.1.4/rdoc/classes/PKGConfig.html

ということで、

     /opt/local/lib/ruby/gems/cache/pkg-config-1.1.4.gem     誤
     /opt/local/lib/ruby/gems/1.8/cache/pkg-config-1.1.4.gem 正

gemの場所のrubyのバージョン有無が異なる。手元でソースからインストールしたバージョン有りのほうが正しい。

原因

port:rubyやport:ruby19がインストールされていない環境で、"port install rb-pkg-config"などいっぺんに依存でインストールしようとしたときにこうなる。要するにbuildbot。

optionsで定義されたグローバル変数をdefaultを使用してprocで値を与えたとき、実際の例で言うと、

   set ruby.gemdir         ${prefix}/lib/ruby${ruby.branch}/gems/${ruby.api_version}

で使ってるruby.api_versionは

   default ruby.api_version    {[ruby.extract_config ruby_version]}

   proc ruby.extract_config {var {default ""}} {
       global ruby.bin
       if {[catch {set val [exec ${ruby.bin} -e "require 'rbconfig';puts RbConfig::CONFIG\[\"${var}\"\]"]}]} {
           set val ${default}
       }
       return $val
   }

のように定義されており、その値の初回評価時に実際にコマンド(ruby-1.9, ruby-1.8, ...)が実行されて値が設定される。

つまり、r112446の変更前の

   destroot {
       system -W ${worksrcpath} "${ruby.gem} install --local --force --install-dir ${destroot}${ruby.gemdir} ${distpath}/${distname}.gem"
       :

は実際のdestroot時に評価される(他で${ruby.api_version}を使用していなければ)のに対し、変更後の

   destroot.args   --local --force --install-dir ${destroot}${ruby.gemdir}

はPortfileの処理時に値を評価してしまう。このタイミングではruby.extract_configで使用するruby本体がまだインストールされていない可能性があり、そのとき初期値を与えていないので""と空文字に落ちてしまう。

その結果、ソースからインストールすると正しい結果になるのに、ビルド済みのファイルをサーバからとってくるとおかしい、という状況になっていた。

対策

  • ruby.extract_configには常に正しいデフォルト値を与える必要がある。
  • ruby.branchセット時に毎度defaultでruby.*の値を設定し直す。ruby.api_versionとかは手書きのswitchで導出する。

という対策を行った。

  • {[ruby.extract_config ]}を使った値は、build {}やdestroot {}などのアクション中では使えるが、configure.argsなどの値には使えないものとする。

という選択肢もあって、そっちのほうが簡単だし意味的には正当なんだけど、それもなんか実装の都合だよなあ。。と思って今回の対処とした。

でもさ、常に正しい初期値を与える必要があるならさ、そもそもruby.extract_config使う必要なくね?