やまごや

皆で Minecraft か Diablo3 (PC版) をやろう

gem install knife-solo で Error

Chef 実践入門を読み始めた。

Chef実践入門――コードによるインフラ構成の自動化:書籍案内|技術評論社

P.30 に記載されている gem knife-solo install を行った際に、
自身の環境(Max OS X 10.9.5)だと Error が発生したので内容、原因、対応をメモ。

Error 内容

$ gem install knife-solo

Building native extensions.  This could take a while...
ERROR:  Error installing knife-solo:
ERROR: Failed to build gem native extension.

/Users/kenya/.rbenv/versions/2.1.1/bin/ruby extconf.rb
creating Makefile
/Users/kenya/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/libyajl2-1.1.0/ext/libyajl2
extconf.rb:104:in `makemakefiles': unhandled exception
from extconf.rb:138:in `<main>'

extconf failed, exit code 1

Gem files will remain installed in /Users/kenya/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/libyajl2-1.1.0 for inspection.
Results logged to /Users/kenya/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/extensions/x86_64-darwin-13/2.1.0-static/libyajl2-1.1.0/gem_make.out

Error 原因

Error が発生している extconf.rb ファイルを開いてみる。

# extconf.rb

require 'rbconfig'
require 'fileutils'

if ENV["USE_SYSTEM_LIBYAJL2"]
  File.open("Makefile", "w+") do |f|
    f.write <<EOF
# dummy Makefile when we're not really installing
all:
\ttrue

install:
\ttrue
EOF
  end
  exit(0)
end

module Libyajl2Build
  class BuildError < StandardError; end

  LIBYAJL2_VENDOR_DIR = File.expand_path("../vendor/yajl", __FILE__).freeze

  PREFIX = File.expand_path("../../../lib/libyajl2/vendored-libyajl2", __FILE__).freeze

  def self.windows?
    !!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
  end

  def self.libyajl2_vendor_dir
    LIBYAJL2_VENDOR_DIR
  end

  def self.prefix
    PREFIX
  end

  def self.deps
    require 'mkmf'
  end

  def self.setup_env
    RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']

    # set some sane defaults
    if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc|clang/
      # magic flags copied from upstream yajl build system (-std=c99 is necessary for older gcc)
      $CFLAGS << " -std=c99 -pedantic -Wpointer-arith -Wno-format-y2k -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wextra  -Wundef -Wwrite-strings -Wold-style-definition -Wredundant-decls -Wno-unused-parameter -Wno-sign-compare -Wmissing-prototypes"
      $CFLAGS << " -O2"  # match what the upstream uses for optimization

      # create the implib on windows
      if windows?
        $LDFLAGS << " -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--out-implib=libyajldll.a -Wl,--output-def,libyajl.def"
      end
    end

    $CFLAGS << " -DNDEBUG"
  end

  def self.makemakefiles
    if RUBY_PLATFORM == "java"
      File.open("Makefile", "w+") do |f|
        f.write <<EOF
CC = gcc
TARGET = libyajl
DLLIB = $(TARGET).so
CFLAGS =  -I. -I../../../../ext/libyajl2 -fPIC -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration  -fPIC -std=c99 -pedantic -Wpointer-arith -Wno-format-y2k -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wextra  -Wundef -Wwrite-strings -Wold-style-definition -Wredundant-decls -Wno-unused-parameter -Wno-sign-compare -Wmissing-prototypes -O2 -DNDEBUG
LDFLAGS = -L. -fstack-protector -rdynamic
#LIBS = -lpthread -ldl -lcrypt -lm -lc
LIBS = -lpthread -ldl -lm -lc
OBJS = yajl_alloc.o yajl_tree.o yajl_gen.o yajl_buf.o yajl.o yajl_encode.o yajl_lex.o yajl_parser.o yajl_version.o

all: $(DLLIB)

$(DLLIB): $(OBJS)
\t$(CC) -shared -o $(DLLIB) $(OBJS) $(LDFLAGS) $(LIBS)

%.o: ../../../../ext/libyajl2/%.c
\t$(COMPILE.c) $(OUTPUT_OPTION) $<

install:
\tmkdir -p #{prefix}/lib
\tcp $(DLLIB) #{prefix}/lib/$(DLLIB)
\tmkdir -p #{prefix}/include/yajl
\tcp yajl/*.h #{prefix}/include/yajl
EOF
      end
    else
      deps
      setup_env
      dir_config("libyajl")
      create_makefile("libyajl")

      # on windows the Makefile will try to export Init_libyajl which is wrong because we aren't a ruby lib.
      # i could not figure out how to tell mkmf.rb to stop being so helpful, so instead will just patch it here.
      if windows?
        makefile = IO.read("Makefile")
        makefile.gsub!(/\$\(DEFFILE\)/, '')
        File.open("Makefile", 'w+') {|f| f.write(makefile) }
      end

      system("pwd")
      # we cheat and build it right away...
      system("make >make.out 2>&1") || raise # rubinius doesn't like the output this generates
      # ...so we can hack up what install does later and copy over the include files

      File.open("Makefile", "w+") do |f|
        f.write <<EOF
TARGET = libyajl
DLLIB = $(TARGET).#{RbConfig::MAKEFILE_CONFIG['DLEXT']}
all:

EOF
        if windows?
          f.write <<EOF
install:
\tmkdir -p #{prefix}/lib
\tcp libyajl.so #{prefix}/lib/libyajl.so
\tcp libyajldll.a #{prefix}/lib/libyajldll.a
\tcp libyajl.def #{prefix}/lib/libyajl.def
\tmkdir -p #{prefix}/include/yajl
\tcp yajl/*.h #{prefix}/include/yajl
EOF
        else
          f.write <<EOF
install:
\tmkdir -p #{prefix}/lib
\tcp $(DLLIB) #{prefix}/lib/$(DLLIB)
\tmkdir -p #{prefix}/include/yajl
\tcp yajl/*.h #{prefix}/include/yajl
EOF
        end
end
    end
  end
end

Libyajl2Build.makemakefiles

該当の 104 行目を見てみると ruby から make コマンドを叩いていた。
周辺のコードも読めないなりに読んでみる。

makefile を作成時に C プログラムをコンパイルするプログラムに gcc を設定していることが解る。
エラーメッセージは unhandled exception。

そもそもの事前準備が整ってないのでは?と思いだす。
gcc コマンド使えるのかなと思って、gcc の version 確認をしてみることに。

Error 対応

sudo gcc --version

You have not agreed to the Xcode license agreements. You must agree to both license agreements below in order to use Xcode.

Hit the Enter key to view the license agreements at '/Applications/Xcode.app/Contents/Resources/English.lproj/License.rtf'

~ 長いので途中を省略 ~

By typing 'agree' you are agreeing to the terms of the software license agreements. Type 'print' to print them or anything else to cancel, [agree, print, cancel]

Xcode のライセンスに同意してなかったようだ。間抜けだなー。
あとは、言われた通りに Enter key を押下して英語の同意書を確認していく。
最後まで確認していくと、同意をするのなら agree と入力して下さいとあるので agree と入力。

gcc のバージョンを確認後、再度 gem knife-solo install を実行。
今度は無事にインストールされた。

# gcc のバージョン確認
$ sudo gcc --version

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.51) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

# knife-solo のインストール
$ gem install knife-solo
Thanks for installing knife-solo!

If you run into any issues please let us know at:
  https://github.com/matschaffer/knife-solo/issues

If you are upgrading knife-solo please uninstall any old versions by
running `gem clean knife-solo` to avoid any errors.

See http://bit.ly/CHEF-3255 for more information on the knife bug
that causes this.
Successfully installed knife-solo-0.4.2
Parsing documentation for knife-solo-0.4.2
Installing ri documentation for knife-solo-0.4.2
Done installing documentation for knife-solo after 1 seconds
1 gem installed

# knife が正常にインストールされているか確認
$ gem list | grep knife

knife-solo (0.4.2)

rbenv を使用している場合は rbenv rehash を忘れずに。
rbenv rehash を忘れがちな場合は、下記をインストールするのもありかと思った。

Ruby - rbenv を使っているなら rbenv-gem-rehash を使おう - Qiita